配色: 字号:
Android中AIDL实现进程通信
2017-01-20 | 阅:  转:  |  分享 
  
Android中AIDL实现进程通信



AIDL概述



之前的博客《Android中通过Messenger与Service实现进程间双向通信》演示了如何通过Messenger实现与Service进行跨进程通信,即IPC。但用Messenger实现的IPC存在一点不足:Service内部维护着一个Messenger,Messenger内部又维护着一个Hanlder,当多个client向该Service发送Message时,这些Message需要依次进入Hanlder的消息队列中,Hanlder只能处理完一个Message之后,再从消息队列中取出下一个进行处理,这样Service相当于只能对客户端传来的Message进行串行执行,而不能并行执行。如果想让Service跨进程并发处理客户端发来的请求,那么就需要使用AIDL。



AIDL的全称是AndroidInterfaceDefinitionLanguage,直译过来就是Android接口定义语言。在Android中,正常情况下,不同进程是不能互相访问对方的内存数据的。为了实现进程间内存访问,可以将一个进程中的内存数据分解成Android认识的原子类型,然后在另一个进程中将原子类型的数据重新组合成对象。编写将原子类型的数据重新组合成对象的代码比较费时,Android通过AIDL可以实现该功能。



其实Messenger和AIDL并不是对立的,Messenger内部也是基于AIDL实现的。当使用Messenger的时候,Service会用一个Handler串行执行客户端发来的多个请求,即单线程处理;当直接使用AIDL的时候,Service会维护一个线程池,用该线程池并发处理客户端发来的多个请求。



再次强调一下,如果你想实现跨进程通信,但是并不关注多线程并发处理,那么你只需要使用Messenger即可,无需编写AIDL代码,只有当你想让Service并发处理客户端的请求的时候,才需要编写AIDL代码。



AIDL之Service端实现



使用AIDL的典型案例就是一个App的Activity启动了一个Service,并且让该Service运行在一个新的进程中,这种在新进程中的Service我们可以叫做远程Service,该Service会从服务器拿到最新的数据,客户端可以调用Service相应的方法获得最新数据,更近一步的话,Service拿到最新数据之后可以主动向客户端发送。



要想使用AIDL,要同时在Service和client都编写相应的代码。



在Service端,首先要创建.aidl文件,然后需要实现.aidl中定义的各种方法。具体如下:



首先要创建.aidl文件



在AndroidStudio中右键创建AIDL文件,该文件其实就是定义一个接口Interface,和定义一般的Java接口差不多,需要在该接口中定义一些方法,这些方法就是允许客户端跨进程调用的方法,我创建了IRemoteService.aidl文件,如下所示:



//IRemoteInterface.aidl

packagecom.ispring.aidldemo;



importcom.ispring.aidldemo.IRemoteInterfaceCallback;



interfaceIRemoteInterface{

//获取Service运行的进程ID

intgetPid();



//从Service中获取最新的数据

intgetData();



//通过向Service中注册回调,可以实现Service主动向客户端推送数据

voidregisterCallback(IRemoteInterfaceCallbackcb);



//删除注册的回调

voidunregisterCallback(IRemoteInterfaceCallbackcb);

}

我们在上面的.aidl文件中,定义了四个方法供客户端调用,通过getPid()方法,可以获取Service运行的进程ID;通过getData()方法,可以让客户端从Service获取最新的数据;registerCallback和unregisterCallback是用来进行注册、反注册客户端回调的,后面会详细解释。



AIDL中定义的方法可以接受如下的参数类型作为形参:



所有的基本类型,例如int,long,char,boolean等等



String、CharSequence、List、Map



在编写完上述IRemoteInterface.aidl文件后,我们编译一下Project,AndroidStudio会自动帮我们生成一个IRemoteInterface.java文件,如下所示(对于下面的代码,我们无需关注具体实现,大家了解一下即可):



/

Thisfileisauto-generated.DONOTMODIFY.

Originalfile:D:\\iWork\\AndroidStudioProjects\\AidlDemo\\app\\src\\main\\aidl\\com\\ispring\\aidldemo\\IRemoteInterface.aidl

/

packagecom.ispring.aidldemo;

publicinterfaceIRemoteInterfaceextendsandroid.os.IInterface{

/Local-sideIPCimplementationstubclass./

publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.ispring.aidldemo.IRemoteInterface{

privatestaticfinaljava.lang.StringDESCRIPTOR="com.ispring.aidldemo.IRemoteInterface";

/Constructthestubatattachittotheinterface./

publicStub(){

this.attachInterface(this,DESCRIPTOR);

}

/

CastanIBinderobjectintoancom.ispring.aidldemo.IRemoteInterfaceinterface,

generatingaproxyifneeded.

/

publicstaticcom.ispring.aidldemo.IRemoteInterfaceasInterface(android.os.IBinderobj){

if((obj==null)){

returnnull;

}

android.os.IInterfaceiin=obj.queryLocalInterface(DESCRIPTOR);

if(((iin!=null)&&(iininstanceofcom.ispring.aidldemo.IRemoteInterface))){

return((com.ispring.aidldemo.IRemoteInterface)iin);

}

returnnewcom.ispring.aidldemo.IRemoteInterface.Stub.Proxy(obj);

}



@Overridepublicandroid.os.IBinderasBinder(){

returnthis;

}



@OverridepublicbooleanonTransact(intcode,android.os.Parceldata,android.os.Parcelreply,intflags)throwsandroid.os.RemoteException{

switch(code){

caseINTERFACE_TRANSACTION:

{

reply.writeString(DESCRIPTOR);

returntrue;

}

caseTRANSACTION_getPid:

{

data.enforceInterface(DESCRIPTOR);

int_result=this.getPid();

reply.writeNoException();

reply.writeInt(_result);

returntrue;

}

caseTRANSACTION_getData:

{

data.enforceInterface(DESCRIPTOR);

int_result=this.getData();

reply.writeNoException();

reply.writeInt(_result);

returntrue;

}

caseTRANSACTION_registerCallback:

{

data.enforceInterface(DESCRIPTOR);

com.ispring.aidldemo.IRemoteInterfaceCallback_arg0;

_arg0=com.ispring.aidldemo.IRemoteInterfaceCallback.Stub.asInterface(data.readStrongBinder());

this.registerCallback(_arg0);

reply.writeNoException();

returntrue;

}

caseTRANSACTION_unregisterCallback:

{

data.enforceInterface(DESCRIPTOR);

com.ispring.aidldemo.IRemoteInterfaceCallback_arg0;

_arg0=com.ispring.aidldemo.IRemoteInterfaceCallback.Stub.asInterface(data.readStrongBinder());

this.unregisterCallback(_arg0);

reply.writeNoException();

returntrue;

}

}



returnsuper.onTransact(code,data,reply,flags);

}



privatestaticclassProxyimplementscom.ispring.aidldemo.IRemoteInterface{

privateandroid.os.IBindermRemote;

Proxy(android.os.IBinderremote){

mRemote=remote;

}



@Overridepublicandroid.os.IBinderasBinder(){

returnmRemote;

}



publicjava.lang.StringgetInterfaceDescriptor(){

returnDESCRIPTOR;

}



@OverridepublicintgetPid()throwsandroid.os.RemoteException{

android.os.Parcel_data=android.os.Parcel.obtain();

android.os.Parcel_reply=android.os.Parcel.obtain();

int_result;

try{

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_getPid,_data,_reply,0);

_reply.readException();

_result=_reply.readInt();

}finally{

_reply.recycle();

_data.recycle();

}

return_result;

}



@OverridepublicintgetData()throwsandroid.os.RemoteException{

android.os.Parcel_data=android.os.Parcel.obtain();

android.os.Parcel_reply=android.os.Parcel.obtain();

int_result;

try{

_data.writeInterfaceToken(DESCRIPTOR);

mRemote.transact(Stub.TRANSACTION_getData,_data,_reply,0);

_reply.readException();

_result=_reply.readInt();

}finally{

_reply.recycle();

_data.recycle();

}

return_result;

}



@OverridepublicvoidregisterCallback(com.ispring.aidldemo.IRemoteInterfaceCallbackcb)throwsandroid.os.RemoteException{

android.os.Parcel_data=android.os.Parcel.obtain();

android.os.Parcel_reply=android.os.Parcel.obtain();

try{

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));

mRemote.transact(Stub.TRANSACTION_registerCallback,_data,_reply,0);

_reply.readException();

}finally{

_reply.recycle();

_data.recycle();

}

}





@OverridepublicvoidunregisterCallback(com.ispring.aidldemo.IRemoteInterfaceCallbackcb)throwsandroid.os.RemoteException{

android.os.Parcel_data=android.os.Parcel.obtain();

android.os.Parcel_reply=android.os.Parcel.obtain();

try{

_data.writeInterfaceToken(DESCRIPTOR);

_data.writeStrongBinder((((cb!=null))?(cb.asBinder()):(null)));

mRemote.transact(Stub.TRANSACTION_unregisterCallback,_data,_reply,0);

_reply.readException();

}finally{

_reply.recycle();

_data.recycle();

}

}

}



staticfinalintTRANSACTION_getPid=(android.os.IBinder.FIRST_CALL_TRANSACTION+0);

staticfinalintTRANSACTION_getData=(android.os.IBinder.FIRST_CALL_TRANSACTION+1);

staticfinalintTRANSACTION_registerCallback=(android.os.IBinder.FIRST_CALL_TRANSACTION+2);

staticfinalintTRANSACTION_unregisterCallback=(android.os.IBinder.FIRST_CALL_TRANSACTION+3);

}



publicintgetPid()throwsandroid.os.RemoteException;



publicintgetData()throwsandroid.os.RemoteException;



publicvoidregisterCallback(com.ispring.aidldemo.IRemoteInterfaceCallbackcb)throwsandroid.os.RemoteException;



publicvoidunregisterCallback(com.ispring.aidldemo.IRemoteInterfaceCallbackcb)throwsandroid.os.RemoteException;

对于上面的代码,我们无需关注具体实现,我们只需要知道以下几点即可:



IRemoteInterface.java中,定义了名为IRemoteInterface的interface,其对应着IRemoteInterface.aidl定义的接口,囊括了IRemoteInterface.aidl中定义的方法。IRemoteInterface继承自android.os.IInterface,如下所示:



publicinterfaceIRemoteInterfaceextendsandroid.os.IInterface

IRemoteInterface接口中有一个静态内部类Stub。Stub是个抽象类,继承自android.os.Binder,且实现了IRemoteInterface接口,如下所示:



publicstaticabstractclassStubextendsandroid.os.Binderimplementscom.ispring.aidldemo.IRemoteInterface

Sub类中有一个asInterface方法,该方法非常重要,其接收一个android.os.IBinder类型的对象,返回IRemoteInterface类型,其方法签名如下定义:



publicstaticcom.ispring.aidldemo.IRemoteInterfaceasInterface(android.os.IBinderobj)

实现AIDL所定义的接口



我们定义了一个RemoteService类,其继承自Service,该类表示客户端要调用的远程服务,我们在AndroidManifest.xml文件中对该Service进行如下配置:




android:name=".RemoteService"

android:enabled="true"

android:process=":remote"/>

我们通过android:process=":remote",让Service运行在一个新的进程中,而不是原有App的进程,其新进程的名字是remote,当然也可以是其他的名称。remote前面的冒号表示该进程被当前的App独享,是该App的私有进程;如果去掉remote前面的冒号,那么则表示该进程是被多个App所共享的。



RemoteService源码如下所示:



packagecom.ispring.aidldemo;



importandroid.app.Service;

importandroid.content.Intent;

importandroid.os.;

importandroid.util.Log;



importjava.util.Random;



publicclassRemoteServiceextendsService{



privateRandomrandom=newRandom();



//callbacks存储了所有注册过的客户端回调

privatefinalRemoteCallbackListcallbacks=newRemoteCallbackList();



privatestaticfinalintMSG_REPORT_DATA=0;



//该handler用来每隔一秒主动向所有注册过回调的客户端发送信息

privateHandlerhandler=newHandler(){

@Override

publicvoidhandleMessage(Messagemsg){

switch(msg.what){

caseMSG_REPORT_DATA:

//开始广播,获取客户端的数量

finalintn=callbacks.beginBroadcast();

intdata=random.nextInt(100);

for(inti=0;i
try{

//遍历客户单回调

IRemoteInterfaceCallbackcallback=callbacks.getBroadcastItem(i);

//执行我们自定义的dataChanged方法,客户端会受到信息

Log.i("DemoLog","RemoteService:handleMessage->callback.dataChanged(data),PID="+android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

callback.dataChanged(data);

}catch(RemoteExceptione){

e.printStackTrace();

}

}

//结束广播

callbacks.finishBroadcast();

//构建新的Message,延迟1秒发送,这样handler每隔一秒都会受到Message

MessagependingMsg=obtainMessage(MSG_REPORT_DATA);

sendMessageDelawww.tt951.comyed(pendingMsg,1000);

break;

default:

super.handleMessage(msg);

}

}

};





//我们要实现

IRemoteInterface.Stubbinder=newIRemoteInterface.Stub(){

@Override

publicintgetPid()throwsRemoteException{

returnandroid.os.Process.myPid();

}



@Override

publicintgetData()throwsRemoteException{

Log.i("DemoLog","RemoteService:binder->getData,PID="+android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

returnrandom.nextInt(100);

}



@Override

publicvoidregisterCallback(IRemoteInterfaceCallbackcb)throwsRemoteException{

Log.i("DemoLog","RemoteService:binder->registerCallback,PID="+android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

if(cb!=null){

//注册客户端回调

callbacks.register(cb);

}

}



@Override

publicvoidunregisterCallback(IRemoteInterfaceCallbackcb)throwsRemoteException{

Log.i("DemoLog","RemoteService:binder->unregisterCallback,PID="+android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

if(cb!=null){

//反注册客户端回调

callbacks.unregister(cb);

}

}

};



publicRemoteService(){

}



@Override

publicvoidonCreate(){

super.onCreate();

handler.sendEmptyMessage(MSG_REPORT_DATA);

Log.i("DemoLog","RemoteService->onCreate,PID="+android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

}



@Override

publicIBinderonBind(Intentintent){

returnbinder;

}



@Override

publicvoidonDestroy(){

Log.i("DemoLog","RemoteService->onDestroy,PID="+android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());



//反注册所有的客户端回调,并且不再接收新的客户端回调

callbacks.kill();



//移除peddingmessage,停止message循环,防止内存泄露

handler.removeMessages(MSG_REPORT_DATA);



super.onDestroy();

}

我们对上述部分代码进行说明:



首先我们通过IRemoteInterface.Stubbinder=newIRemoteInterface.Stub()实例化了一个IRemoteInterface.Stub对象。之前我们提到IRemoteInterface.Stub继承自android.os.Binder,所以该实例也就是一个Binder,并且其实现了接口IRemoteInterface定义的抽象方法。



然后在Service的onBind接口中将上述binder对象返回,这样才能让客户端通过bindService方法调用Service。



我们在后面会解释handler、客户端回调相关的代码片段。



AIDL之客户端实现



通常调用Service的客户端是Activity,我在MainActivity中调用RemoteService,其界面如下所示:

这里写图片描述



客户端MainActivity源码如下所示:



packagecom.ispring.aidldemo;



importandroid.content.ComponentName;

importandroid.content.Intent;

importandroid.content.ServiceConnection;

importandroid.os.;

importandroid.app.Activity;

importandroid.util.Log;

importandroid.view.View;

importandroid.widget.Button;

importandroid.widget.TextView;

importandroid.widget.Toast;



publicclassMainActivityextendsActivityimplementsButton.OnClickListener{



privateButtonbtnBindService;

privateButtonbtnGetData;

privateButtonbtnRegister;

privateButtonbtnUnregister;

privateButtonbtnUnbindService;

privateButtonbtnKillProcess;

privateTextViewtextView;

privatebooleanisRegistered=false;

privateIRemoteInterfaceremoteInterface;

privatestaticfinalintMSG_GET_DATA=0;

privatestaticfinalintMSG_DATA_CHANGED=1;



//handler用于在主线程中更新UI

privateHandlerhandler=newHandler(){

@Override

publicvoidhandleMessage(Messagemsg){

switch(msg.what){

caseMSG_GET_DATA:

//通过远程服务的getData方法获取数据

Toast.makeText(MainActivity.this,"Data:"+msg.arg1,Toast.LENGTH_LONG).show();

break;

caseMSG_DATA_CHANGED:

//远程服务通过客户端回调向客户端推送数据

textView.setText("Receivedatafromservice:"+msg.arg1);

break;

default:

super.handleMessage(msg);

}

}

};





privateServiceConnectionsc=newServiceConnection(){

@Override

publicvoidonServiceConnected(ComponentNamename,IBinderbinder){

Log.i("DemoLog","MainActivity:ServiceConnection->onServiceConnected");

remoteInterface=IRemoteInterface.Stub.asInterface(binder);



//更新UI状态

btnBindService.setEnabled(false);

btnGetData.setEnabled(true);

btnRegister.setEnabled(true);

btnUnregister.setEnabled(false);

btnUnbindService.setEnabled(true);

btnKillProcess.setEnabled(true);

textView.setText("已连接到RemoteService");

}



@Override

publicvoidonServiceDisconnected(ComponentNamename){

Log.i("DemoLog","MainActivity:ServiceConnection->onServiceDisconnected");

remoteInterface=null;



//更新UI状态

btnBindService.setEnabled(true);

btnGetData.setEnabled(false);

btnRegister.setEnabled(false);

btnUnregister.setEnabled(false);

btnUnbindService.setEnabled(false);

btnKillProcess.setEnabled(false);

textView.setText("与RemoteService断开连接");

}

};



//callback为客户端向RemoteService注册的回调接口

privateIRemoteInterfaceCallbackcallback=newIRemoteInterfaceCallback.Stub(){

@Override

publicvoiddataChanged(intdata)throwsRemoteException{

Log.i("DemoLog","MainActivity:callback->dataChanged,data:"+data+",PID="++android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

Messagemsg=Message.obtain();

msg.what=MSG_DATA_CHANGED;

msg.arg1=data;

handler.sendMessage(msg);

}

};





@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btnBindService=(Button)findViewById(R.id.btnBindService);

btnGetData=(Button)findViewById(R.id.btnGetData);

btnRegister=(Button)findViewById(R.id.btnRegister);

btnUnregister=(Button)findViewById(R.id.btnUnregister);

btnUnbindService=(Button)findViewById(R.id.btnUnbindService);

btnKillProcess=(Button)findViewById(R.id.btnKillProcess);

textView=(TextView)findViewById(R.id.textView);

}



@Override

publicvoidonClick(Viewv){

switch(v.getId()){

caseR.id.btnBindService:

bindService();

break;

caseR.id.btnGetData:

getData();

break;

caseR.id.btnRegister:

registerCallback();

break;

caseR.id.btnUnregister:

unregisterCallback();

break;

caseR.id.btnUnbindService:

unbindService();

break;

caseR.id.btnKillProcess:

killServiceProcess();

break;

}

}



privatevoidbindService(){

if(remoteInterface!=null){

return;

}

Intentintent=newIntent(MainActivity.this,RemoteService.class);

bindService(intent,sc,BIND_AUTO_CREATE);

}



privatevoidgetData(){

if(remoteInterface==null){

return;

}



try{

Log.i("DemoLog","MainActivity->getData");

intdata=remoteInterface.getData();

Messagemsg=Message.obtain();

msg.what=MainActivity.MSG_GET_DATA;

msg.arg1=data;

handler.sendMessage(msg);

}catch(RemoteExceptione){

e.printStackTrace();

}

}



privatevoidregisterCallback(){

if(remoteInterface==null||isRegistered){

return;

}

try{

Log.i("DemoLog","MainActivity->registerCallback");

//客户端向远程服务注册客户端回调

remoteInterface.registerCallback(callback);

isRegistered=true;



//更新UI

btnRegister.setEnabled(false);

btnUnregister.setEnabled(true);

Toast.makeText(this,"已向Service注册Callback",Toast.LENGTH_LONG).show();

}catch(RemoteExceptione){

e.printStackTrace();

}

}



privatevoidunregisterCallback(){

if(remoteInterface==null||!isRegistered){

return;

}

try{

Log.i("DemoLog","MainActivity->unregisterCallback");

//远程服务反注册客户端回调

remoteInterface.unregisterCallback(callback);

isRegistered=false;



//更新UI

btnRegister.setEnabled(true);

btnUnregister.setEnabled(false);

}catch(RemoteExceptione){

e.printStackTrace();

}

}



privatevoidunbindService(){

if(remoteInterface==null){

return;

}



unregisterCallback();

unbindService(sc);

remoteInterface=null;



//更新UI状态

btnBindService.setEnabled(true);

btnGetData.setEnabled(false);

btnRegister.setEnabled(false);

btnUnregister.setEnabled(false);

btnUnbindService.setEnabled(false);

btnKillProcess.setEnabled(false);

textView.setText("与RemoteService断开连接");

}



privatevoidkillServiceProcess(){

if(remoteInterface==null){

return;

}



try{

Log.i("DemoLog","MainActivity->killServiceProcess");

//获取远程服务的进程ID,并杀死远程服务

intpid=remoteInterface.getPid();

android.os.Process.killProcess(pid);

remoteInterface=null;

//Service进程被杀死后,会触发onServiceDisconnected的执行

}catch(RemoteExceptione){

e.printStackTrace();

}

bindService



与正常调用bindService一样,首先我们实例化了一个ServiceConnection对象sc,然后将其传递给了bindService方法,即bindService(intent,sc,BIND_AUTO_CREATE)。



点击bindService按钮后,输出如下所示:

这里写图片描述



通过上图可以看到,我们的MainActivity是运行在进程com.ispring.aidldemo中,其进程ID为4986。在调用了bindService之后,RemoteService被实例化,RemoteService运行在名为com.ispring.aidldemo:remote的进程中,其进程ID是6106。



RemoteService创建成功后,会通过其onBind方法返回IBinder对象,将其传递给MainActivity中定义的ServiceConnection对象的onServiceConnected方法中,该方法是在客户端的主线程中执行的。在该方法中通过IRemoteInterface.Stub的asInterface(binder)方法将IBinder对象转换成远程接口IRemoteInterface.Stub,这样我们就相当于得到了远程接口的实例,并将其通过字段remoteInterface进行存储,可以通过该实例调用接口中定义的方法。该远程接口相当于是远程服务RemoteService的代言人,所以下面我们不区分远程接口和远程服务的概念,可以把二者看成一回事,这样可以简化描述。



getData

点击getData按钮,可以通过intdata=remoteInterface.getData()执行远程服务的getData方法,可以从远程服务中获取最新的数据。我们在完成单击之后过一段时间再次单击getData按钮,这两次单击的输出如下所示:

这里写图片描述



我们可以看到,两次单击导致RemoteService中binder对象的getData方法被调用了两次,且都是在RemoteService的进程(进程ID为6106)中执行的,不是在客户端进程执行。不过第一次是在Binder_1线程中执行,第二次是在Binder_2线程中执行。



当客户端调用远程服务AIDL的方法时,这些远程服务的方法是在远程服务的进程中执行的,但是在哪个线程中是不确定的。远程服务内部会维护一个线程池,从线程池中随机取出一个线程执行客户端调用的方法。



当远程服务的getData方法执行完毕后,客户端会得到返回的结果,然后客户端可以根据该值做相应处理,在本例中,我们将得到的data通过Message的形式发送到handler,然后在该handler中更新UI。



registerCallback

通过上面的getData方法可以让客户端从远程服务获取最新的数据,但是很多情况下我们希望远程服务的最新数据发生变化后直接推送给我们客户端,即客户端和远程服务通过观察者模式进行数据更新的通信,此时我们就需要客户端回调。



我们在IRemoteInterface中定义了与回调相关的两个方法,registerCallback和unregisterCallback。registerCallback用于向远程服务中注册回调,可以实现Service主动向客户端推送数据。unregisterCallback用于删除注册的回调。二者的方法签名如下所示:



//通过向Service中注册回调,可以实现Service主动向客户端推送数据

voidregisterCallback(IRemoteInterfaceCallbackcb);



//删除注册的回调

voidunregisterCallback(IRemoteInterfaceCallbackcb);

这两个方法都接收一个IRemoteInterfaceCallback参数,IRemoteInterfaceCallback就是一个回调接口,该接口中定义的方法可以被远程服务调用。IRemoteInterfaceCallback也是由AIDL定义的,IRemoteInterfaceCallback.aidl文件如下所示:



packagecom.ispring.aidldemo;



//关键字oneway表示该接口下面的所有方法不会造成客户端阻塞等待服务端方法执行完成

onewayinterfaceIRemoteInterfaceCallback{

voiddataChanged(intdata);

IRemoteInterfaceCallback.aidl被编译后,会产生IRemoteInterfaceCallback.java文件,其定义了IRemoteInterfaceCallback接口和抽象类IRemoteInterfaceCallback.Stub,IRemoteInterfaceCallback.Stub继承自IBinder,且实现了IRemoteInwww.baiyuewang.netterfaceCallback接口。通过我们程序中的这两个aidl文件的编译,我们就会发现每个aidl文件编译后,AndroidStudio都会默认帮我们添加一个其对应的Stub类,我们要和这个Stub类进行操作。



我们在客户单MainActivity通过如下代码实现了IRemoteInterfaceCallback.Stub的一个实例:



//callback为客户端向RemoteService注册的回调接口

privateIRemoteInterfaceCallbackcallback=newIRemoteInterfaceCallback.Stub(){

@Override

publicvoiddataChanged(intdata)throwsRemoteException{

Log.i("DemoLog","MainActivity:callback->dataChanged,data:"+data+",PID="++android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

Messagemsg=Message.obtain();

msg.what=MSG_DATA_CHANGED;

msg.arg1=data;

handler.sendMessage(msg);

}

当我们点击registerCallback按钮后,执行了代码remoteInterface.registerCallback(callback),输出如下所示:

这里写图片描述



我们对以上过程进行一下讲解:



我们将callback作为参数传递给远程服务的registerCallback方法。



RemoteService会执行binder的registerCallback方法,代码如下所示:



@Override

publicvoidregisterCallback(IRemoteInterfaceCallbackcb)throwsRemoteException{

Log.i("DemoLog","RemoteService:binder->registerCallback,PID="+android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

if(cb!=null){

//注册客户端回调

callbacks.register(cb);

}



在RemoteService中,我们定义了一个RemoteCallbackList类型的callbacks参数,用它存储所有注册过的客户端回调。通过callbacks.register(cb),将客户端传递过来的回调参数注册到callbacks中。



我们在RemoteService中定义了一个handler,每隔一秒发送一个Message,模拟数据发生了变化,然后向所有的客户端主动发送更新后的数据。handler中主要的相关代码如下所示:



//开始广播,获取客户端的数量

finalintn=callbacks.beginBroadcast();

intdata=random.nextInt(100);

for(inti=0;i
try{

//遍历客户单回调

IRemoteInterfaceCallbackcallback=callbacks.getBroadcastItem(i);

//执行我们自定义的dataChanged方法,客户端会受到信息

Log.i("DemoLog","RemoteService:handleMessage->callback.dataChanged(data),PID="+android.os.Process.myPid()+",Thread="+Thread.currentThread().getName());

callback.dataChanged(data);

}catch(RemoteExceptione){

e.printStackTrace();

}

}

//结束广播

callbacks.finishBroadcast();

//构建新的Message,延迟1秒发送,这样handler每隔一秒都会受到Message

MessagependingMsg=obtainMessage(MSG_REPORT_DATA);

sendMessageDelayed(pendingMsg,1000);

通过callbacks的beginBroadcast()方法获取注册的客户端的数量,然后通过callbacks的getBroadcastItem()方法遍历所有的客户端回调,通过调用客户端回调dataChanged方法,向客户端主动发送数据,客户端MainActivity中的callback的dataChanged会回调被执行,且接收到远程服务传入的数据。向所有客户端推送数据完成后,调用callbacks的finishBroadcast()方法。



由于我们每隔一秒就发送一条Message数据,所以每隔一秒RemoteService就会向所有的客户端主动发送一遍数据,所以每隔一秒MainActivity中callback的dataChanged方法就被执行一次。我们通过上面的控制台输出可以发现,MainActivity中callback的dataChanged方法内部的代码是在MainActivity所在的进程(进程ID为4986)中执行的,但是在哪个线程中执行是不确定的,有时候是线程Binder_3,有时候是线程Binder_2,有时候是线程Binder_1。



客户端的AIDL回调方法内的代码是在客户端的进程中执行的,客户端中也维护了一个线程池,从该线程池中随机取出一个线程执行客户端回调方法,所以不要在AIDL的客户端回调方法中更新UI,需要通过hanlder更新UI。



unregisterCallback

通过单击unregisterCallback按钮,可以反注册客户端回调,这样客户端就不会再收到远程服务发送的数据,单击unregisterCallback按钮后控制台输出如下:



这里写图片描述



在远程服务的进程中执行binder的unregisterCallback方法,通过callbacks的unregister()方法将传入的客户端回调从callbacks中删除。



unbindService

在客户端与远程服务处于绑定的情况下,点击unbindService按钮之后,远程服务执行onDestroy方法,远程服务销毁,控制台输出如下所示:



com.ispring.aidldemo:remoteI/DemoLog:RemoteService->onDestroy,PID=6106,Thread=main

1

1

需要注意的是,单纯调用unbindService方法没有触发ServiceConnection的onServiceDisconnected()方法的执行。



KillServiceProcess

在客户端与远程服务处于绑定的情况下,点击KillServiceProcess按钮,可以杀死远程服务的进程,控制台输出如下所示:



这里写图片描述



首先通过我们在IRemoteService.aidl中定义的getPid()方法获取远程服务的进程ID,然后通过android.os.Process.killProcess(pid)杀死远程服务进程。需要注意的是,android.os.Process.killProcess()方法并不是可以杀死任意的进程,你只能杀死你自己App的进程以及在你的App中所创建的新的进程(比如此例中的远程服务的进程就是在App自身的进程中创建的)。



oneway关键字



当客户端调用AIDL中的方法时,默认情况下客户端会阻塞式地等待远程服务执行完毕,然后客户端才能继续执行代码。比如在IRemoteService.aidl中定义的getData方法,如果该方法在远程服务中执行了较长时间才返回了数据,那么客户端也要等待该时间。很多时候,AIDL中的方法不需要返回具体的数据,这种情况下为了避免客户端一直等待远程方法执行完成,我们就可以将aidl接口声明为oneway,声明为oneway的AIDL接口中的所有方法在调用时都不会阻塞,具体来说,调用了远程方法后,不用等着远程方法执行完毕,会立即返回继续执行后面的代码,所以正因为此特性,oneway接口下面的方法都必须是返回void类型,不能返回其他类型的数据。大部分情况下,我们一般将客户端的回调接口AIDL定义为oneway的,这样远程服务调用回调接口中的方法时不会阻塞远程服务后面代码的执行。



总结



如果你想实现跨进程通信,但是并不关注多线程并发处理,那么你只需要使用Messenger即可,无需编写AIDL代码,只有当你想让Service并发处理客户端的请求的时候,才需要编写AIDL代码。



当客户端调用远程服务AIDL的方法时,这些远程服务的方法是在远程服务的进程中执行的,但是在哪个线程中是不确定的。远程服务内部会维护一个线程池,从线程池中随机取出一个线程执行客户端调用的方法。



通过定义客户端回调AIDL接口,可以在客户端和远程服务之间创建观察者模式,让远程服务主动向客户端发送数据。并且应该将客户端AIDL接口声明为oneway,这样不会阻塞远程服务代码的执行。



客户端的AIDL回调方法内的代码是在客户端的进程中执行的,客户端中也维护了一个线程池,从该线程池中随机取出一个线程执行客户端回调方法,所以不要在AIDL的客户端回调方法中更新UI,需要通过hanlder更新UI。

献花(0)
+1
(本文系thedust79首藏)