Android中的跨进程通信AIDL、Binder源码详解

来源:互联网 发布:手机移动办公软件 编辑:程序博客网 时间:2024/06/05 19:26

Binder

Android系统是基于Linux内核的,而Linux内核继承和兼容了丰富的Unix系统进程间通信(IPC)机制。有传统的管道(Pipe)、信号(Signal)和跟踪(Trace),后来又增加了命令管道(Named Pipe)。

为了更好地支持商业应用中的事务处理,在AT&T的Unix系统V中,又增加了三种称为“System V IPC”的进程间通信机制,分别是:报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)。后来BSD Unix对“System V IPC”机制进行了重要的扩充,提供了名为Socket的进程间通信机制。

但是Android系统没有采用上述提到的各种进程间通信机制,而是采用Binder机制(难道因为移动设备硬件性能较差、内存较低?)。其实Binder也不是Android提出来的一套新机制,它是基于OpenBinder来实现的。OpenBinder的作者Dianne Hackborn现在就在Google工作,负责Android平台的开发工作。

Binder的底层机制很复杂,这篇文章主要讲应用层的东西,通过分析AIDL源码来搞清楚Binder的工作流程。

Binder实现了IBinder接口,Android规定了使用Binder来连接客户端、中间件和服务端,所以每个角色都和Binder相关:
1、客户端,成功绑定服务端后只会返回IBinder对象,所以客户端所有的操作都必须依靠IBinder对象;
2、中间件,收到客户端的指令后,会调用transact()方法,然后回调onTransact()方法,在该方法中调用服务端的方法;
3、服务端,收到中间件的指令后,执行具体方法,并通过中间件Binder机制把结果返回给客户端;

AIDL

AIDL是是Android Interface Definition Language的简称,翻译过来就是安卓进程间通信语言。既然是一门“语言”,就有相应的语法,下面流程分析时会对语法进行简单的介绍,这里暂时不做介绍。

这个AIDL“语言”严格来说肯定不算一门语言,它只是规定了一些简单的语法,开发者遵循这些语法,就能比较方便、快捷地完成进程间通信的开发工作,其实说它是一个插件更合适。

我们知道,Android进程间通信使用的是Binder机制,我们可以自定义Binder来完成进程间通信,但开发起来比较复杂。Google为了方便我们开发,就提供了AIDL这种插件,我们只需要遵循一些规则,使用比较小的工作量就能完成进程间通信了

AIDL流程分析

先列出完整的使用方法及代码,在代码注释中分析工作流程(下面流程所遵循的规范,就可以认为是“语法”)。

AIDL的使用

第一步,在服务端app中,src/main目录下创建aidl目录,创建com.wk.kupart包,在该包下创建Fun.aidl文件,并声明两个方法:

package com.wk.kupart;interface Fun {    int add(in int x, in int y);    int reduce(in int x, in int y);}

第二步,clean工程,clean完成后,build/generated/source/aidl/debug目录下就会自动生成com.wk.kupart包,该包下有个Fun.java文件,这就是系统根据上面的Fun.aidl文件自动生成的java文件;

第三步,创建MyService类继承自Service,实现onBind()方法,在该方法中返回Fun.stub对象:

// 这是manifest中Service的注册,这些属性都要写,用于远程绑定<service    android:name=".MyService"    android:enabled="true"    android:exported="true">        <intent-filter>            <action android:name="com.wk.funservice"/>        </intent-filter></service>// java代码public class MyService extends Service {    @Override    public void onCreate() {        super.onCreate();    }    @Override    public void onDestroy() {        super.onDestroy();    }    @Override    public IBinder onBind(Intent intent) {        // 返回Stub对象,该类实现了IBinder接口        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        return super.onUnbind(intent);    }    // 自动生成的Fun.java文件中,有静态内部类Stub,这里new出Stub对象,并实现我们之前定义的方法    private final Fun.Stub mBinder = new Fun.Stub() {        @Override        public int add(int x, int y) throws RemoteException {            return x + y;        }        @Override        public int reduce(int x, int y) throws RemoteException {            return x - y;        }    };}

第四步,在客户端app中,重复上面的步骤一和步骤二。注意包名、类名、声明的方法等要跟服务端app中的一模一样;

第五步,在客户端app中的任一Activity中绑定远程Service:

public class NewActivity extends Activity {    private Fun mFun;    private ServiceConnection mServiceConn = new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {            Log.i("wk", "onServiceDisconnected");        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            Log.i("wk", "onServiceConnected");            mFun = Fun.Stub.asInterface(service);            try {                int x = mFun.add(2, 5);            } catch (RemoteException e) {                e.printStackTrace();            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_new);        // Android5.0后,绑定Service必须显示绑定,如果要隐士绑定,需要设置Package        Intent intent = new Intent();        // 这里的action就是远程Service在manifest中注册时填写的action        intent.setAction("com.wk.funservice");        // 这里的package是远程Service的packageName        intent.setPackage("com.wk.serviceapp");        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);    }}

这样,利用AIDL语法完成的进程间通信就完成了,接下来,我们分析下工作流程。

AIDL源码分析

1、客户端调用服务端的方法:

public void onServiceConnected(ComponentName name, IBinder service) {    Log.i("wk", "onServiceConnected");    // 获取服务端返回的IBinder对象,从而生成Fun对象。    mFun = Fun.Stub.asInterface(service);    try {        // 调用Fun对象的add()方法        int x = mFun.add(2, 5);    } catch (RemoteException e) {        e.printStackTrace();    }}


2、客户端调用的具体实现:

Fun.java在服务端和客户端的代码是一模一样的,其中一部分是服务端会执行,一部分是客户端会执行,为了使逻辑更清晰,下面的代码就只列出客户端需要的主流程部分:

public interface Fun extends android.os.IInterface{    // Fun的静态内部类,集成自Binder,实现了Fun接口(即实现了我们在Fun.aidl中声明的方法)    public static abstract class Stub extends android.os.Binder implements com.wk.serviceapp.Fun    {        private static final java.lang.String DESCRIPTOR = "com.wk.serviceapp.Fun";        // 通过int值来标识客户端调的到底是哪个方法        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_reduce = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);        // 通过IBinder对象,生成Fun对象        public static com.wk.serviceapp.Fun asInterface(android.os.IBinder obj)        {            if ((obj==null)) {                return null;            }            // 如果是同进程调用,就返回iin,不会走进程间通信的流程            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin!=null)&&(iin instanceof com.wk.serviceapp.Fun))) {                return ((com.wk.serviceapp.Fun)iin);            }            // 如果跨进程调用,就会返回这个Proxy对象(Proxy集成自Fun)。再往下面分析Proxy            return new com.wk.serviceapp.Fun.Stub.Proxy(obj);        }        // Proxy是Stub的内部类,也实现了Fun接口,所以可以被当作Fun类型返回        private static class Proxy implements com.wk.serviceapp.Fun        {            // 绑定Service成功后,服务端返回的IBinder对象            private android.os.IBinder mRemote;            // 构造方法中,把服务端返回的IBinder对象参数赋值给mRemote            Proxy(android.os.IBinder remote)            {                mRemote = remote;            }            // 可以看到,上面调用的add()方法就是调用的Proxy中的add()方法            @Override public int add(int x, int y) throws android.os.RemoteException            {                // 远程调用时的参数记录在_data中,执行返回的结果会记录在_reply中                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                int _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    // 将参数x、y记录到_data中                    _data.writeInt(x);                    _data.writeInt(y);                    // 调用mRemote的transact()方法,把“调用的是哪个方法”、“参数”、“存放执行结果的容器”传过去                    // transact()方法又会回调mRemote的onTransact()方法,实现真正地调用,分析到服务端时再分析                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);                    _reply.readException();                    // 服务端执行完毕后,把结果放在_reply“容器”中,然后从_reply中取出结果                    _result = _reply.readInt();                }                finally {                    _reply.recycle();                    _data.recycle();                }                // 返回服务端的执行结果                return _result;            }            @Override public int reduce(int x, int y) throws android.os.RemoteException            {                android.os.Parcel _data = android.os.Parcel.obtain();                android.os.Parcel _reply = android.os.Parcel.obtain();                int _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    _data.writeInt(x);                    _data.writeInt(y);                    mRemote.transact(Stub.TRANSACTION_reduce, _data, _reply, 0);                    _reply.readException();                    _result = _reply.readInt();                }                finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }        }    }    public int add(int x, int y) throws android.os.RemoteException;    public int reduce(int x, int y) throws android.os.RemoteException;}


3、客户端调用mRemote.transact()方法将指令传递到服务端:

上面已经提到,收到客户端指令后,会回调mRemote的onTransact()方法,那就要先搞清楚mRemote是谁。mRemote是Service绑定成功后返回的IBinder对象,所以看Service源码:

public class MyService extends Service {    @Override    public IBinder onBind(Intent intent) {        // 可以看到,返回的就是Fun.Stub对象。Stub实现了Fun接口,所以我们需要实现add、reduce方法        return mBinder;    }    private final Fun.Stub mBinder = new Fun.Stub() {        @Override        public int add(int x, int y) throws RemoteException {            return x + y;        }        @Override        public int reduce(int x, int y) throws RemoteException {            return x - y;        }    };}

这样,我们继续回到Fun.java中看Stub相关的代码。同上,省略客户端流程相关代码,只列出服务端主流程相关代码。

public interface HAH extends android.os.IInterface{    public static abstract class Stub extends android.os.Binder implements com.wk.serviceapp.Fun    {        private static final java.lang.String DESCRIPTOR = "com.wk.serviceapp.Fun";        // 上面提到过的,用于标识客户端调的是哪个方法        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);        static final int TRANSACTION_reduce = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);        public Stub()        {            this.attachInterface(this, DESCRIPTOR);        }        @Override public android.os.IBinder asBinder()        {            return this;        }        // 客户端发出指令后,最终会回调这个方法来处理        // code是标识客户端调的是哪个方法,data是执行方法的参数, reply是存放执行结果的容器        @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_add:                {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    // 从data中取出第一个参数                    _arg0 = data.readInt();                    int _arg1;                    // 从data中取出第二个参数                    _arg1 = data.readInt();                    // 调用this的add()方法。this就是Stub,add方法的具体实现在Service类中由我们自己实现                    int _result = this.add(_arg0, _arg1);                    reply.writeNoException();                    // 把执行结果放入容器中,并通知客户端                    reply.writeInt(_result);                    return true;                }                case TRANSACTION_reduce:                {                    data.enforceInterface(DESCRIPTOR);                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    int _result = this.reduce(_arg0, _arg1);                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }    }    public int add(int x, int y) throws android.os.RemoteException;    public int reduce(int x, int y) throws android.os.RemoteException;}

自此,一个完成的客户端调用、中间件传递、服务端执行、通过中间件通知客户端流程就分析完毕了。

自定义进程间通信

我们现在已经知道了Binder进程间通信的原理,那我们完全可以自己定义一套,比AIDL更简单、逻辑更清晰。

客户端绑定Service之后只能得到一个IBinder对象,服务端被绑定成功后也只能返回IBinder对象,所以我们自定义也只能基于Binder(基于Messenger的进程间通信本质也是通过Binder,这里暂时不分析)。

第一步,服务端的实现:

我们知道接收到客户端指令后会调用Binder的onTransact()方法,所以我们只需要自定义onTransact()方法,把执行结果返回给客户端即可。

public class MyService extends Service {    @Override    public void onCreate() {        super.onCreate();    }    @Override    public void onDestroy() {        super.onDestroy();    }    @Override    public IBinder onBind(Intent intent) {        // 返回我们自定义的IBinder对象        return mBinder;    }    @Override    public boolean onUnbind(Intent intent) {        return super.onUnbind(intent);    }    private MyBinder mBinder = new MyBinder();    // 自定义MBinder,集成自Binder    private class MyBinder extends Binder    {        @Override        // 重写onTransact()方法,处理客户端的指令        protected boolean onTransact(int code, Parcel data, Parcel reply,                                     int flags) throws RemoteException        {            switch (code)            {                // 如果指令code为0x01,就执行加法。这个指令自己定义就行                case 0x01:                {                    // 这个是token,客户端调用时需要设置token与下面值一样                    data.enforceInterface("com.wk.serviceapp.myservice");                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    // 执行加法                    int _result = _arg0 + _arg1;                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }                // 如果指令code为0x02,就执行减法                case 0x02:                {                    data.enforceInterface("com.wk.serviceapp.myservice");                    int _arg0;                    _arg0 = data.readInt();                    int _arg1;                    _arg1 = data.readInt();                    // 执行减法                    int _result = _arg0 - _arg1;                    reply.writeNoException();                    reply.writeInt(_result);                    return true;                }            }            return super.onTransact(code, data, reply, flags);        }    }}

在manifest中注册与AIDL一致,就不说了。


第二步,客户端实现:

客户端要做的更简单,就是调用IBinder的Transact()方法通知服务端就可以了。

public class NewActivity extends Activity {    private ServiceConnection mServiceConn = new ServiceConnection() {        @Override        public void onServiceDisconnected(ComponentName name) {        }        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            android.os.Parcel _data = android.os.Parcel.obtain();            android.os.Parcel _reply = android.os.Parcel.obtain();            int _result;            try {                // 设置token                _data.writeInterfaceToken("com.wk.serviceapp.myservice");                // 设置add()方法中的参数1、参数2                _data.writeInt(2);                _data.writeInt(5);                // 调用transact()方法                service.transact(0x01, _data, _reply, 0);                _reply.readException();                // 获取结果                _result = _reply.readInt();                Log.i("wk", "result:" + _result);            } catch (RemoteException e) {                e.printStackTrace();            } finally {                _reply.recycle();                _data.recycle();            }        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_new);        Intent intent = new Intent();        intent.setAction("com.wk.serviceapp.add");        intent.setPackage("com.wk.serviceapp");        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);    }}

就这么简单,一个基于Binder的自定义的进程间通信就完成了,没有aidl文件,没有自动生成的java文件,也不需要注意aidl繁琐的规则。

注意一点,可以看到,客户端调用服务端的方法后,就是同步等待服务端的执行结果,所以不能执行耗时操作。如果要执行耗时操作,就把transact()方法的调用放在子线程中。


跨进程通信总结

Android中跨进程通信方式主要有这么三种:
1、文件共享、Socket;
2、Bundle;
3、AIDL(IBinder)、ContentProvider和Messenger

第一种是几乎所有操作系统都通用的。

第二种是Android独有的数据传输方式,启动一个新组件时通过Intent携带Bundle参数。

第三种方式是Google为Android设计的跨进程通信方式,其核心就是,客户端绑定服务端,服务端返回IBinder对象给客户端,通过这个IBinder建立联系、进行通信。ContentProvider和Messenger本质都是通过这种方式来实现通信,只是做了一些封装,让开发者使用起来更加方便。

Bundle和IBinder传输的数据必须是可序列化的,甚至部分方式(Messenger中的msg.obj)只能传输Parcelable类型,这就和上篇讲的序列化联系起来了。

1 0
原创粉丝点击