AIDL介绍

AIDL背景

在Android平台上,一个进程通常无法访问另一个进程的内存,所以想要跨进程访问的话,需要将要传递的数据分解为系统可以支持和识别的基本单元,有序的经过进程的边界。因为这个操作十分繁琐,所以Android使用AIDL来解决这个问题。AIDL就是用于生成两个进程间进行进程间通信的(IPC)代码,面向开发简化这个过程。

注:只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。无论如何,在实现 AIDL 之前,请您务必理解绑定服务。

定义AIDL接口

  • 创建一个.aidl文件。
  • 实现接口,此接口具有一个名为 stub 的内部抽象类,用于扩展 Binder 类并实现 aild 定义的接口。
  • 向客户端公开接口,实现 Service 并重写 onBind() 返回给 Stub 类的实现。

创建aidl文件

默认情况下,aidl支持的数据类型有:

  • 基本数据类型( int, long, char, boolean, double等等)
  • String 和 CharSequence
  • List : 每个元素都必须被 aidl 支持,只支持ArrayList
  • Map : 每个元素都必须被 aidl 支持,支持支HashMap
  • Parcelable : 所有实现了 Parcelable 接口的对象
  • aidl : 所有 aidl 接口本身也可以在 aidl 文件中使用

一个 aidl 文件例子 :

// IStudentManager.aidl
package com.seaicelin.aidl;

import com.seaicelin.aidl.Student;

interface IStudentManager {
    List<Student> getStudentList();
    void addStudent(in Student student);
}

Android Studio会自动生成一个 aidl 文件相对应的 java 接口文件,它包含一个名为 stub 的抽象类和他的代理类 proxy。

package com.seaicelin.aidl;

public interface IStudentManager extends android.os.IInterface {

    //Local-side IPC implementation stub class.
    public static abstract class Stub extends android.os.Binder implements com.seaicelin.aidl.IStudentManager {
        private static final java.lang.String DESCRIPTOR = "com.seaicelin.aidl.IStudentManager";

        //Construct the stub at attach it to the interface.
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        //Cast an IBinder object into an com.seaicelin.aidl.IStudentManager   
        //interface,generating a proxy if needed.
        public static com.seaicelin.aidl.IStudentManager asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.seaicelin.aidl.IStudentManager))) {
                return ((com.seaicelin.aidl.IStudentManager) iin);
            }
            return new com.seaicelin.aidl.IStudentManager.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_getStudentList: {
                    data.enforceInterface(DESCRIPTOR);
                    java.util.List<com.seaicelin.aidl.Student> _result = this.getStudentList();
                    reply.writeNoException();
                    reply.writeTypedList(_result);
                    return true;
                }
                case TRANSACTION_addStudent: {
                    data.enforceInterface(DESCRIPTOR);
                    com.seaicelin.aidl.Student _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = com.seaicelin.aidl.Student.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.addStudent(_arg0);
                    reply.writeNoException();
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements com.seaicelin.aidl.IStudentManager {
            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.util.List<com.seaicelin.aidl.Student> getStudentList() throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                java.util.List<com.seaicelin.aidl.Student> _result;
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    mRemote.transact(Stub.TRANSACTION_getStudentList, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.createTypedArrayList(com.seaicelin.aidl.Student.CREATOR);
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

            @Override
            public void addStudent(com.seaicelin.aidl.Student student) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((student != null)) {
                        _data.writeInt(1);
                        student.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_addStudent, _data, _reply, 0);
                    _reply.readException();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_getStudentList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
        static final int TRANSACTION_addStudent = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
    }

    public java.util.List<com.seaicelin.aidl.Student> getStudentList() throws android.os.RemoteException;

    public void addStudent(com.seaicelin.aidl.Student student) throws android.os.RemoteException;
}

远程服务端service实现接口

服务端实现上文中提到的 stub 抽象类,实现 aidl 接口。这里的 mBinder 是 Stub 的一个 实例,用于定义服务的 RPC 接口;在下一步中,将向客户端公开该实例,以便客户端能与服务端进行交互。

<service
    android:name=".StudentManagerService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>

public class StudentManagerService extends Service {

    private static final String TAG = "[StudentManagerService]";

    private CopyOnWriteArrayList<Student> mStudentList = new CopyOnWriteArrayList<>();

    public StudentManagerService() {
    }

    @Override
    public void onCreate(){
        super.onCreate();
        mStudentList.add(new Student(1, "seaicelin1"));
        mStudentList.add(new Student(2, "seaicelin2"));
        mStudentList.add(new Student(3, "seaicelin3"));
    }

    //实现aidl生成的java文件的Stub抽象类并实现aidl方法
    //aidl方法在Binder线程池中执行,多个客户端同时连接时,需要处理线程同步
    //CopyOnWriteArrayList支持并发读写,支持线程同步
    private Binder mBinder = new IStudentManager.Stub(){

        @Override
        public List<Student> getStudentList() throws RemoteException {
            return mStudentList;
        }

        @Override
        public void addStudent(Student student) throws RemoteException {
            if(mStudentList.contains(student))
                return;
            mStudentList.add(student);
        }
    };

    @Override
    public IBinder onBind(Intent intent) {
        return mBinder;
    }

}

客户端实现

在 service 内部实现接口后,需要向客户端公开该接口,以便客户端绑定。服务端需要创建一个 service 并实现 onBind() ,返回一个实例,这个实例实现了上文提到的 Stub 抽象类。

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private static final String TAG = "[MainActivity]";
    private Button mBtnService;
    private IStudentManager mStudentManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mBtnService = (Button) findViewById(R.id.button);
        mBtnService.setOnClickListener(this);

    }
    //创建 ServiceConnection 对象
    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            mStudentManager = IStudentManager.Stub.asInterface(service);
            try {
                List<Student> list = mStudentManager.getStudentList();

                for(int i = 0; i < list.size(); i++){
                    Log.e(TAG, "query student list " + list.get(i).toString());
                }

                Student student = new Student(4, "seaicelin4");
                mStudentManager.addStudent(student);

                List<Student> newList = mStudentManager.getStudentList();

                for(int i = 0; i < newList.size(); i++){
                    Log.e(TAG, "after add student, query student list " + newList.get(i).toString());
                }

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };


    @Override
    public void onClick(View v) {
        Intent intent = new Intent(this, StudentManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
        startService(intent);
        Log.e(TAG, "onClick");
    }

    @Override
    protected void onDestroy(){
        unbindService(mConnection);
        super.onDestroy();
    }
}

由此,客户端创建完毕,查询服务端的有多少个学生,然后增加一个学生,最后再次打印服务端的学生数。我们可以通过 log 来看看是否已经能够正常使用。

03-25 09:17:09.117 31597-31597/com.seaicelin.aidl E/[MainActivity]: onClick
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 1 name = seaicelin1
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 2 name = seaicelin2
03-25 09:17:09.129 31597-31597/com.seaicelin.aidl E/[MainActivity]: query student list id = 3 name = seaicelin3
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 1 name = seaicelin1
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 2 name = seaicelin2
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 3 name = seaicelin3
03-25 09:17:09.133 31597-31597/com.seaicelin.aidl E/[MainActivity]: after add student, query student list id = 4 name = seaicelin4

到这里,已经成功使用 aidl 进行跨进程通信,aidl 的基本使用介绍完毕。


总结调用 IPC 方法

用谷歌官方文档作为总结吧,调用类必须执行以下步骤,才能调用使用 AIDL 定义的远程接口:

  • 在项目 src/ 目录中加入 .aidl 文件。
  • 声明一个 IBinder 接口实例(基于 AIDL 生成)。
  • 实现 ServiceConnection。
  • 调用 Context.bindService(),以传入您的 ServiceConnection 实现。
  • 在您的 onServiceConnected() 实现中,您将收到一个 IBinder 实例(名为 service)。调用 YourInterfaceName.Stub.asInterface((IBinder)service),以将返回的参数转换为 YourInterface 类型。
  • 调用您在接口上定义的方法。您应该始终捕获 DeadObjectException 异常,它们是在连接中断时引发的;这将是远程方法引发的唯一异常。
  • 如需断开连接,请使用您的接口实例调用 Context.unbindService()。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值