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类型,这就和上篇讲的序列化联系起来了。
- Android中的跨进程通信AIDL、Binder源码详解
- Android 中的Binder跨进程通信机制与AIDL
- android 利用AIDL实现Binder跨进程通信
- Android-服务跨进程通信(Binder/Messenger/AIDL)
- android之AIDL跨进程通信详解
- Android中的Binder跨进程通信机制
- Android 跨进程通信--Binder
- Android跨进程通信-AIDL
- Android跨进程通信-AIDL
- Android AIDL跨进程通信
- Android AIDL跨进程通信
- Android 跨进程通信: AIDL
- Android:AIDL跨进程通信
- Android应用内跨进程通信AIDL实例与源码
- 图文详解 Android Binder跨进程通信机制 原理
- Android跨进程通信-AIDL详解示例(client+server)
- Android Binder跨进程通信原理分析
- Android Binder跨进程通信原理分析
- 一个自由定制可展开的的Material Design风格FloatingActionButton
- 剑指offer(java代码)——用两个栈实现队列
- Git学习记录
- C语言编写班级花名册(链表版)
- Java\Android进阶代码提炼师之代码重构原则
- Android中的跨进程通信AIDL、Binder源码详解
- ubuntu开机自动运行用Qt写的程序
- HashTable和HashMap的区别详解
- openstack创建一个完整的项目过程
- CodeForces 734 A.Anton and Danik(水~)
- 数据结构与算法(十四)红黑树
- 禅道-从windows7迁移至ubuntu14.04之四
- 如何在一台半新半旧的linux上装methylkit
- Problem B: 时间和日期类(II)