本文首先概述了Android的进程间通讯的Binder机制,而后结合一个AIDL的例子,对Binder机制进行了解析。java
咱们知道,在Android app中的众多activity,service等组件能够运行在同一进程中,也能够运行在不一样进程中。当组件运行在同一进程中进行通讯就显得比较简单,在以前的Android线程间通讯机制中已经讲过了;而当它们运行在不一样的进程中时,就须要使用咱们本文中所要介绍的Binder机制了。android
Binder做为一种进程间通讯机制,负责提供远程调用的功能(RPC),它的系统组件主要包括四种:Client, Server, ServiceManager, Binder Driver. 它们之间的关系以下图所示:app
从图中咱们能够看出,Client, Server, ServiceManager运行在系统的用户态,而Binder Driver运行在内核态。为了完成Client端到Server端的通讯任务,用户空间的须要操做Binder Driver提供的/dev/binder文件来完成交互。那么ServiceManager的工做是什么呢?ServiceManager负责管理Server并向Client端提供一个Server的代理接口(proxy)。经过代理接口中定义的方法,Client端就可使用Server端提供的服务了。整个过程以下:ide
另外,Client端与Server端的调用过程是同步的,即在Server返回结果以前,Client端是阻塞的。调用过程以下所示:函数
OK,下面咱们经过AIDL的一个例子来分析一下以上过程时如何进行的。ui
首先,建立一个aidl文件“ICalculateAIDL.aidl”,这个接口里面定义了要对外提供的服务,咱们这里定义了计算加法和减法的函数:this
package com.cqumonk.calculate.aidl; interface ICalculateAIDL { int add(int a,int b); int minus(int a,int b); }
建立好后,rebuild一下咱们的项目,能够生成同名的java文件。这是一个接口,里面包含了一个名为Stub的静态抽象类,以及咱们在aidl文件中定义的加减法函数。里面详细代码在后面分析,接口文件结构以下:spa
而后咱们须要实现服务端方面的功能,建立一个service,咱们顺手把生命周期中各方法都打印出来,并完成加法和减法函数的实现:线程
public class CalculateService extends Service { private static final String TAG="SERVER"; public CalculateService() { } @Override public void onCreate() { super.onCreate(); Log.e(TAG,"OnCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { Log.e(TAG,"onStartCommand"); return super.onStartCommand(intent, flags, startId); } @Override public IBinder onBind(Intent intent) { Log.e(TAG,"onBind"); return mBinder; } @Override public void onDestroy() { super.onDestroy(); Log.e(TAG,"onDestroy"); } @Override public boolean onUnbind(Intent intent) { Log.e(TAG,"onUnbind"); return super.onUnbind(intent); } @Override public void onRebind(Intent intent) { Log.e(TAG,"onRebind"); super.onRebind(intent); } private final ICalculateAIDL.Stub mBinder=new ICalculateAIDL.Stub(){ @Override public int add(int a, int b) throws RemoteException { return a+b; } @Override public int minus(int a, int b) throws RemoteException { return a-b; } }; }
在service中,咱们使用刚刚生成的ICalculateAIDL.Stub静态抽象类建立了一个Binder对象,实现了其中有关计算的业务方法,并在onBind方法中返回它。另外咱们还须要在manifest文件中对该服务进行注册:代理
<service android:name=".CalculateService" android:enabled="true" android:exported="true" > <intent-filter> <action android:name="com.cqumonk.adil.calculate"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service>
这时候,咱们的服务端工做就完成了。咱们开始编写客户端的代码来调用这个服务。首先,咱们定义一个activity,包含四个按钮:
而后咱们能够在activity中启动以前定义好的service并调用它所提供的服务:
public class MainActivity extends Activity implements View.OnClickListener { Button mBind; Button mUnbind; Button mAdd; Button mMinus; TextView txt_res; private static final String TAG="CLIENT"; private ICalculateAIDL mCalculateAIDL; private boolean binded=false; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mBind= (Button) findViewById(R.id.btn_bind); mUnbind= (Button) findViewById(R.id.btn_unbind); mAdd= (Button) findViewById(R.id.btn_add); mMinus= (Button) findViewById(R.id.btn_minus); txt_res= (TextView) findViewById(R.id.txt_res); mBind.setOnClickListener(this); mUnbind.setOnClickListener(this); mAdd.setOnClickListener(this); mMinus.setOnClickListener(this); } @Override protected void onStop() { super.onStop(); unbind(); } private void unbind(){ if (binded){ unbindService(mConnection); binded=false; } } @Override public void onClick(View v) { int id=v.getId(); switch (id){ case R.id.btn_bind: Intent intent=new Intent(); intent.setAction("com.cqumonk.adil.calculate"); bindService(intent,mConnection, Context.BIND_AUTO_CREATE); break; case R.id.btn_unbind: unbind(); break; case R.id.btn_add: if(mCalculateAIDL!=null){ try { int res=mCalculateAIDL.add(3,3); txt_res.setText(res+""); } catch (RemoteException e) { e.printStackTrace(); } }else{ Toast.makeText(this,"please rebind",Toast.LENGTH_SHORT).show(); } break; case R.id.btn_minus: if(mCalculateAIDL!=null){ try { int res=mCalculateAIDL.minus(9,4); txt_res.setText(res+""); } catch (RemoteException e) { e.printStackTrace(); } }else{ Toast.makeText(this,"please rebind",Toast.LENGTH_SHORT).show(); } break; } } private ServiceConnection mConnection=new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.e(TAG,"connect"); binded=true; mCalculateAIDL=ICalculateAIDL.Stub.asInterface(service); } @Override public void onServiceDisconnected(ComponentName name) { Log.e(TAG,"disconnect"); mCalculateAIDL=null; binded=false; } }; }
当咱们点击绑定按钮,观察日志:
点击加法和减法按钮,均可以完成计算并返回值。而后点击解绑按钮:
咱们并未发现有链接断开的日志打印,这时候,咱们继续点击加减法按钮,发现仍然能够完成计算功能,这又是为毛呢?咱们看到unbind和destroy的日志打印,说明链接已经断开,service已经被销毁,可是咱们返回的stub对象仍然是能够继续使用的。而并非说service仍然在运行。
过程分析
首先,咱们调用bindservice方法来启动了service:
一方面链接成功时调用了serviceConnection的onServiceConnected方法。咱们从该方法中获取到一个Binder对象(注意,这个binder并非咱们在service中实现的那个哦。当service时本apk中的service时,这里返回的是同一个binder),咱们经过此binder来与server进行通讯。为了区分,咱们称为clientBinder。
public void onServiceConnected(ComponentName name, IBinder service) { Log.e(TAG,"connect"); binded=true; mCalculateAIDL= ICalculateAIDL.Stub.asInterface(service); }
另外一方面,onBind方法返回了咱们实现的Stub对象,其实也是一个Binder,用于和Client进行通讯。(以前咱们定义了一个aidl接口文件,并根据它生成了ICalculateAIDL接口。这个接口中有咱们定义的两个方法以及一个静态抽象内部类Stub,它是一个Binder的子类。)咱们来看一下Stub是如何定义的:
public static abstract class Stub extends android.os.Binder implements com.cqumonk.calculate.aidl.ICalculateAIDL
它继承了Binder类也实现了ICalculateAIDL接口,咱们实现了定义的add和minus方法。咱们仍然要强调一下它并非clientBinder,在负责与clientBinder进行通讯交互的同时,它也维护了service描述符与服务端service的映射。
咱们在Client中的onServiceConnected里调用了stub对象的asInterface方法,并将以前获得的clientBinder传入:
public static com.cqumonk.calculate.aidl.ICalculateAIDL asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);//根据包名获取本地实现的一个接口的实例,若是是本地service则能够获取到 if (((iin != null) && (iin instanceof com.cqumonk.calculate.aidl.ICalculateAIDL))) { return ((com.cqumonk.calculate.aidl.ICalculateAIDL) iin); //若是获得的实例是ICalculateAIDL的对象,则返回 } return new com.cqumonk.calculate.aidl.ICalculateAIDL.Stub.Proxy(obj);//若是没法获得本地实现的对象则会返回一个代理对象 }
在这个方法中,首先在系统中查找注册的的service,若是没有找到,那么必定是别的apk实现的service,因而返回一个此service的静态代理类对象供Client调用。咱们来看一下这个代理,建立时咱们将clientBinder传入了,同时也它实现了ICalculateAIDL接口:
private static class Proxy implements com.cqumonk.calculate.aidl.ICalculateAIDL { 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 int add(int a, int b) 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(a); //将参数打包 _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); //调用binderDriver的提供的方法将参数发给服务端 _reply.readException(); _result = _reply.readInt(); //读取到返回结果 } finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public int minus(int a, int b) 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(a); _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_minus, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } }
代理中也实现了ICalculateAIDL接口定义的方法,咱们以add方法为例,里面将参数打包发送给Server端。在Server端收到请求后,会调用service中咱们实现的那个stub对象(mBinder)的onTransact方法:
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; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.add(_arg0, _arg1); //调用咱们实现好的方法 reply.writeNoException(); reply.writeInt(_result); //把结果返回 return true; } case TRANSACTION_minus: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.minus(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } } return super.onTransact(code, data, reply, flags); }
调用完成后把结果打包返回给Poxy处理,最后返回给客户端。
由上面的例子咱们能够看出,在跨进程通讯的时候,Client端使用的Poxy里面封装了一个binder与Server端的stub(也是一个binder对象)进行交互,两个binder做为接口调用BinderDriver的transact来发送数据包,以及onTransact接收处理数据包。
经过结合AIDL例子,咱们对Android进程间的通讯机制进行了分析,若是有错误的地方,欢迎指正。