Android Service AIDL

Android 中的各个应用程序都运行在自己的进程中,进程之间一般是无法直接进行数据交换的

而为了实现跨进程,Android 提供了上一章节提到的 Binder 机制,而这个机制使用的接口语言就是: AIDL (Android Interface Definition Language)

AIDL 语法很简单,只是定义两个进程间的通信接口而已

而生成符合通信协议的 Java 代码则是由 Android SDKplatform-tools/aidl.exe 工具生成

生成对应的接口文件在: gen 目录下,一般是 Xxx.java 的接口

在该接口中包含一个 Stub 的内部类,该类中实现了在该类中实现了 IBinder 接口与自定义的通信接口

Sub 类会作为远程 Service 的回调类——实现了 IBinder 接口,所以可作为 ServiceonBind() 方法的返回值

AIDL 接口

开始编写 AIDL 接口文件前,我们先了解一些注意事项

  1. 接口名需要与 aidl 文件名相同

  2. 接口和方法前面 不要加访问权限修饰符 public ,private,protected 等,也不能用 static final

  3. AIDL 默认支持的类型包括 Java基本类型StringListMapCharSequence ,除此之外的其它类型都 需要 import 声明,对于使用自定义类型作为参数或者返回值, 自定义类型需要实现 Parcelable 接口

  4. 自定义类型和 AIDL 生成的其它接口类型在 aidl 描述文件中,应该显式 import,即便在该类和定义的包在同一个包中

Android Studio 创建

  1. Android Studio 创建 AIDL 需要在 main 目录下新建一个 aidl 文件夹

    app 目录上点右键,然后选择 New->Folder->AIDL FOLDER

  2. 接着定义一个和 aidl 包名相同的包

  3. 然后创建一个 aidl 文件

    在包名上点右键,然后选择 New->AIDL 文件

  4. 最后点击 Build->Make Project 编译

AIDL 实现两个进程间的简单通信

先来看看最后的效果图


创建服务端

  1. 创建一个 空的 Android 项目 cn.twle.android.AIDLServer

  2. app 目录下创建 aidl 目录

  3. aidl 目录下新建包 cn.twle.android.aidl

  4. cn.twle.android.aidl 包下新建文件 ILanguage.aidl

    package cn.twle.android.aidl;
    
    interface ILanguage {
        String queryLanguage(int num);
    }
    
  5. 点击 Build->Make Project 编译代码

    打开 ILanguage.java 可以看到里面的代码如下

    /*
     * This file is auto-generated.  DO NOT MODIFY.
     * Original file: /Android/AIDLServer/app/src/main/aidl/cn/twle/android/aidl/ILanguage.aidl
     */
    package cn.twle.android.aidl;
    public interface ILanguage extends android.os.IInterface
    {
    /** Local-side IPC implementation stub class. */
    public static abstract class Stub extends android.os.Binder implements cn.twle.android.aidl.ILanguage
    {
    private static final java.lang.String DESCRIPTOR = "cn.twle.android.aidl.ILanguage";
    /** Construct the stub at attach it to the interface. */
    public Stub()
    {
    this.attachInterface(this, DESCRIPTOR);
    }
    /**
     * Cast an IBinder object into an cn.twle.android.aidl.ILanguage interface,
     * generating a proxy if needed.
     */
    public static cn.twle.android.aidl.ILanguage asInterface(android.os.IBinder obj)
    {
    if ((obj==null)) {
    return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof cn.twle.android.aidl.ILanguage))) {
    return ((cn.twle.android.aidl.ILanguage)iin);
    }
    return new cn.twle.android.aidl.ILanguage.Stub.Proxy(obj);
    }
    @Override public android.os.IBinder asBinder()
    {
    return this;
    }
    @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
    switch (code)
    {
    case INTERFACE_TRANSACTION:
    {
    reply.writeString(DESCRIPTOR);
    return true;
    }
    case TRANSACTION_queryLanguage:
    {
    data.enforceInterface(DESCRIPTOR);
    int _arg0;
    _arg0 = data.readInt();
    java.lang.String _result = this.queryLanguage(_arg0);
    reply.writeNoException();
    reply.writeString(_result);
    return true;
    }
    }
    return super.onTransact(code, data, reply, flags);
    }
    private static class Proxy implements cn.twle.android.aidl.ILanguage
    {
    private android.os.IBinder mRemote;
    Proxy(android.os.IBinder remote)
    {
    mRemote = remote;
    }
    @Override public android.os.IBinder asBinder()
    {
    return mRemote;
    }
    public java.lang.String getInterfaceDescriptor()
    {
    return DESCRIPTOR;
    }
    @Override public java.lang.String queryLanguage(int num) throws android.os.RemoteException
    {
    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();
    java.lang.String _result;
    try {
    _data.writeInterfaceToken(DESCRIPTOR);
    _data.writeInt(num);
    mRemote.transact(Stub.TRANSACTION_queryLanguage, _data, _reply, 0);
    _reply.readException();
    _result = _reply.readString();
    }
    finally {
    _reply.recycle();
    _data.recycle();
    }
    return _result;
    }
    }
    static final int TRANSACTION_queryLanguage = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }
    public java.lang.String queryLanguage(int num) throws android.os.RemoteException;
    }
    

    我们只要关注 asInterface(IBinder) 和我们定义的接口中的 queryLanguage() 方法

    该方法会把 IBinder 类型的对象转换成 ILanguage 类型的,必要时生成一个代理对象返回结果

  6. MainActivity.java 同一目录下新建文件 MsLanguageService.java

    继承 Service 类,同时也自定义了一个 LanguageQueryBinder 类用来继承 ILanguage.Stub

    也就是实现了 IPerson 接口和 IBinder 接口

    然后实例化自定义的 Stub 类,并重写 ServiceonBind() 方法,返回一个 binder 对象

    package cn.twle.android.aidlserver;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.IBinder;
    import android.os.RemoteException;
    import cn.twle.android.aidl.ILanguage.Stub;
    
    public class MsLanguageService extends Service {
    
        private IBinder binder = new LanguageQueryBinder();
        private String[] names = {"python","php","java","kotin","c","swift"};
    
        private String query(int num)
        {
            if(num > 0 && num < 6){
                return names[num - 1];
            }
            return null;
        }
    
        @Override
        public IBinder onBind(Intent intent) {
            return binder;
        }
    
        private final class LanguageQueryBinder extends Stub{
            @Override
            public String queryLanguage(int num) throws RemoteException {
                return query(num);
            }
        }
    }
    
  7. 然后修改 AndroidManifest.xml 文件中注册 Service

    <service android:name=".MsLanguageService">
        <intent-filter>
            <action android:name="cn.twle.android.aidlserver.MsLanguageService" />
            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </service>
    

这里并没有提供 Activity界面,但是改应用提供的 Service 可以供其它 app 来调用

客户端

  1. 创建一个 空的 Android 项目 cn.twle.android.AIDLClient

  2. 重复上面的 [2-5] 步骤创建 ILanguage.java

  3. 先编译一下

  4. 修改 activity_main.xml

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical" >
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="请输入要查询的语言" />
    
        <EditText
            android:id="@+id/ipt_search"
            android:inputType="number"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"/>
        <Button 
            android:id="@+id/btn_query"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="查询"/>
    
        <TextView 
            android:id="@+id/txt_show"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    
    </LinearLayout>
    
  5. 修改 MainActivity.java

    1. 自定义 LanguageConnection 类实现 ServiceConnection 接口

    2. LanguageConnection 对象作为参数,调用 bindService() 绑定远程 Service

      bindService(service,conn,BIND_AUTO_CREATE);
      
      第三个参数是设置如果服务没有启动的话,自动创建
      
    3. 和本地Service不同,绑定远程 Service 的 ServiceConnection 并不能直接获取 Service 的 onBind() 方法返回IBinder 对象,只能返回 onBind() 方法所返回的 代理对象

      需要做如下处理

      iLanguage = ILanguage.Stub.asInterface(service)
      

      再接着完成初始化

    MainActivity.java

    package cn.twle.android.aidlclient;
    
    import android.content.ComponentName;
    import android.content.Intent;
    import android.content.ServiceConnection;
    import android.os.Bundle;
    import android.os.IBinder;
    import android.os.RemoteException;
    import android.support.v7.app.AppCompatActivity;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.TextView;
    
    import cn.twle.android.aidl.ILanguage;
    
    public class MainActivity extends AppCompatActivity implements View.OnClickListener{
    
        private EditText ipt_search;
        private Button btn_query;
        private TextView txt_show;
        private ILanguage iLanguage;
        private LanguageConnection conn = new LanguageConnection();
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            bindViews();
            //绑定远程Service
            Intent service = new Intent("cn.twle.android.aidlserver.MsLanguageService");
            service.setPackage("cn.twle.android.aidlserver");
    
            bindService(service, conn, BIND_AUTO_CREATE);
            btn_query.setOnClickListener(this);
        }
    
        private void bindViews() {
            ipt_search = (EditText) findViewById(R.id.ipt_search);
            btn_query = (Button) findViewById(R.id.btn_query);
            txt_show = (TextView) findViewById(R.id.txt_show);
        }
    
        @Override
        public void onClick(View v) {
            String number = ipt_search.getText().toString();
            int num = Integer.valueOf(number);
            try {
                txt_show.setText(iLanguage.queryLanguage(num));
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            ipt_search.setText("");
        }
    
        private final class LanguageConnection implements ServiceConnection {
            public void onServiceConnected(ComponentName name, IBinder service) {
                iLanguage = ILanguage.Stub.asInterface(service);
    
            }
            public void onServiceDisconnected(ComponentName name) {
                iLanguage = null;
            }
        }
    }
    

接下来先启动 AIDLServivce,然后再启动 AIDLClient

当然也可以直接启动 AIDLClient,也会获得同样效果

Android 基础教程

关于   |   FAQ   |   我们的愿景   |   广告投放   |  博客

  简单教程,简单编程 - IT 入门首选站

Copyright © 2013-2022 简单教程 twle.cn All Rights Reserved.