近期在研究Windows之间的进程管道通信,刚好又发现一篇关于Android的进程通信,写的非常不错,很仔细,注释也全。转载过来收藏。指不定哪天人家就关博客了呢!
AIDL的全称是Android Interface Definition Language,直译过来就是android接口定义语言。在Android中,正常情况下,不同进程是不能互相访问对方的内存数据的。为了实现进程间内存访问,可以将一个进程中的内存数据分解成Android认识的原子类型,然后在另一个进程中将原子类型的数据重新组合成对象。编写将原子类型的数据重新组合成对象的代码比较费时,Android通过AIDL可以实现该功能。
其实Messenger和AIDL并不是对立的,Messenger内部也是基于AIDL实现的。当使用Messenger的时候,Service会用一个Handler串行执行客户端发来的多个请求,即单线程处理;当直接使用AIDL的时候,Service会维护一个线程池,用该线程池并发处理客户端发来的多个请求。
再次强调一下,如果你想实现跨进程通信,但是并不关注多线程并发处理,那么你只需要使用Messenger即可,无需编写AIDL代码,只有当你想让Service并发处理客户端的请求的时候,才需要编写AIDL代码。
要想使用AIDL,要同时在Service和client都编写相应的代码。
在Service端,首先要创建.aidl文件,然后需要实现.aidl中定义的各种方法。具体如下:
首先要创建.aidl文件在Android Studio中右键创建AIDL文件,该文件其实就是定义一个接口Interface,和定义一般的Java接口差不多,需要在该接口中定义一些方法,这些方法就是允许客户端跨进程调用的方法,我创建了IRemoteService.aidl文件,如下所示:
我们在上面的.aidl文件中,定义了四个方法供客户端调用,通过getPid()方法,可以获取Service运行的进程ID;通过getData()方法,可以让客户端从Service获取最新的数据;registerCallback和unregisterCallback是用来进行注册、反注册客户端回调的,后面会详细解释。
AIDL中定义的方法可以接受如下的参数类型作为形参:
所有的基本类型,例如int, long, char, boolean等等 String、CharSequence、List、Map
在编写完上述IRemoteInterface.aidl文件后,我们编译一下Project,Android Studio会自动帮我们生成一个IRemoteInterface.java文件,如下所示(对于下面的代码,我们无需关注具体实现,大家了解一下即可):
对于上面的代码,我们无需关注具体实现,我们只需要知道以下几点即可:
IRemoteInterface.java中,定义了名为IRemoteInterface的interface,其对应着IRemoteInterface.aidl定义的接口,囊括了IRemoteInterface.aidl中定义的方法。IRemoteInterface继承自android.os.IInterface,如下所示:
IRemoteInterface接口中有一个静态内部类Stub。Stub是个抽象类,继承自android.os.Binder,且实现了IRemoteInterface接口
Sub类中有一个asInterface方法,该方法非常重要,其接收一个android.os.IBinder类型的对象,返回IRemoteInterface类型,其方法签名如下定义:
实现AIDL所定义的接口
我们定义了一个RemoteService类,其继承自Service,该类表示客户端要调用的远程服务,我们在AndroidManifest.xml文件中对该Service进行如下配置:
我们通过android:process=":remote",让Service运行在一个新的进程中,而不是原有App的进程,其新进程的名字是remote,当然也可以是其他的名称。remote前面的冒号表示该进程被当前的App独享,是该App的私有进程;如果去掉remote前面的冒号,那么则表示该进程是被多个App所共享的。
RemoteService源码如下所示:
我们对上述部分代码进行说明:
首先我们通过IRemoteInterface.Stub binder = new IRemoteInterface.Stub()实例化了一个IRemoteInterface.Stub对象。之前我们提到IRemoteInterface.Stub继承自android.os.Binder,所以该实例也就是一个Binder,并且其实现了接口IRemoteInterface定义的抽象方法。 然后在Service的onBind接口中将上述binder对象返回,这样才能让客户端通过bindService方法调用Service。
我们在后面会解释handler、客户端回调相关的代码片段。
客户端MainActivity源码如下所示:
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按钮,可以通过int data = remoteInterface.getData()执行远程服务的getData方法,可以从远程服务中获取最新的数据。我们在完成单击之后过一段时间再次单击getData按钮,这两次单击的输出如下所示:
当客户端调用远程服务AIDL的方法时,这些远程服务的方法是在远程服务的进程中执行的,但是在哪个线程中是不确定的。远程服务内部会维护一个线程池,从线程池中随机取出一个线程执行客户端调用的方法。
当远程服务的getData方法执行完毕后,客户端会得到返回的结果,然后客户端可以根据该值做相应处理,在本例中,我们将得到的data通过Message的形式发送到handler,然后在该handler中更新UI。 registerCallback 通过上面的getData方法可以让客户端从远程服务获取最新的数据,但是很多情况下我们希望远程服务的最新数据发生变化后直接推送给我们客户端,即客户端和远程服务通过观察者模式进行数据更新的通信,此时我们就需要客户端回调。我们在IRemoteInterface中定义了与回调相关的两个方法,registerCallback和unregisterCallback。registerCallback用于向远程服务中注册回调,可以实现Service主动向客户端推送数据。unregisterCallback用于删除注册的回调。二者的方法签名如下所示:
这两个方法都接收一个IRemoteInterfaceCallback参数,IRemoteInterfaceCallback就是一个回调接口,该接口中定义的方法可以被远程服务调用。IRemoteInterfaceCallback也是由AIDL定义的,IRemoteInterfaceCallback.aidl文件如下所示:
IRemoteInterfaceCallback.aidl被编译后,会产生IRemoteInterfaceCallback.java文件,其定义了IRemoteInterfaceCallback接口和抽象类IRemoteInterfaceCallback.Stub,IRemoteInterfaceCallback.Stub继承自IBinder,且实现了IRemoteInterfaceCallback接口。通过我们程序中的这两个aidl文件的编译,我们就会发现每个aidl文件编译后,Android Studio都会默认帮我们添加一个其对应的Stub类,我们要和这个Stub类进行操作。
我们在客户单MainActivity通过如下代码实现了IRemoteInterfaceCallback.Stub的一个实例:
当我们点击registerCallback按钮后,执行了代码remoteInterface.registerCallback(callback),输出如下所示:
我们对以上过程进行一下讲解:
我们将callback作为参数传递给远程服务的registerCallback方法。 RemoteService会执行binder的registerCallback方法,代码如下所示:
在RemoteService中,我们定义了一个RemoteCallbackList<IRemoteInterfaceCallback>类型的callbacks参数,用它存储所有注册过的客户端回调。通过callbacks.register(cb),将客户端传递过来的回调参数注册到callbacks中。 我们在RemoteService中定义了一个handler,每隔一秒发送一个Message,模拟数据发生了变化,然后向所有的客户端主动发送更新后的数据。handler中主要的相关代码如下所示:
通过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方法,远程服务销毁,控制台输出如下所示:
首先通过我们在IRemoteService.aidl中定义的getPid()方法获取远程服务的进程ID,然后通过android.os.Process.killProcess(pid)杀死远程服务进程。需要注意的是,android.os.Process.killProcess()方法并不是可以杀死任意的进程,你只能杀死你自己App的进程以及在你的App中所创建的新的进程(比如此例中的远程服务的进程就是在App自身的进程中创建的)。
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的全称是Android Interface Definition Language,直译过来就是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文件在Android Studio中右键创建AIDL文件,该文件其实就是定义一个接口Interface,和定义一般的Java接口差不多,需要在该接口中定义一些方法,这些方法就是允许客户端跨进程调用的方法,我创建了IRemoteService.aidl文件,如下所示:
// IRemoteInterface.aidl package com.ispring.aidldemo; import com.ispring.aidldemo.IRemoteInterfaceCallback; interface IRemoteInterface { //获取Service运行的进程ID int getPid(); //从Service中获取最新的数据 int getData(); //通过向Service中注册回调,可以实现Service主动向客户端推送数据 void registerCallback(IRemoteInterfaceCallback cb); //删除注册的回调 void unregisterCallback(IRemoteInterfaceCallback cb); }
我们在上面的.aidl文件中,定义了四个方法供客户端调用,通过getPid()方法,可以获取Service运行的进程ID;通过getData()方法,可以让客户端从Service获取最新的数据;registerCallback和unregisterCallback是用来进行注册、反注册客户端回调的,后面会详细解释。
AIDL中定义的方法可以接受如下的参数类型作为形参:
所有的基本类型,例如int, long, char, boolean等等 String、CharSequence、List、Map
在编写完上述IRemoteInterface.aidl文件后,我们编译一下Project,Android Studio会自动帮我们生成一个IRemoteInterface.java文件,如下所示(对于下面的代码,我们无需关注具体实现,大家了解一下即可):
/* * This file is auto-generated. DO NOT MODIFY. * Original file: D:\\iWork\\AndroidStudioProjects\\AidlDemo\\app\\src\\main\\aidl\\com\\ispring\\aidldemo\\IRemoteInterface.aidl */ package com.ispring.aidldemo; public interface IRemoteInterface extends android.os.IInterface { /** Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.ispring.aidldemo.IRemoteInterface { private static final java.lang.String DESCRIPTOR = "com.ispring.aidldemo.IRemoteInterface"; /** Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.ispring.aidldemo.IRemoteInterface interface, * generating a proxy if needed. */ public static com.ispring.aidldemo.IRemoteInterface asInterface(android.os.IBinder obj) { if ((obj==null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin!=null)&&(iin instanceof com.ispring.aidldemo.IRemoteInterface))) { return ((com.ispring.aidldemo.IRemoteInterface)iin); } return new com.ispring.aidldemo.IRemoteInterface.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_getPid: { data.enforceInterface(DESCRIPTOR); int _result = this.getPid(); reply.writeNoException(); reply.writeInt(_result); return true; } case TRANSACTION_getData: { data.enforceInterface(DESCRIPTOR); int _result = this.getData(); reply.writeNoException(); reply.writeInt(_result); return true; } case TRANSACTION_registerCallback: { data.enforceInterface(DESCRIPTOR); com.ispring.aidldemo.IRemoteInterfaceCallback _arg0; _arg0 = com.ispring.aidldemo.IRemoteInterfaceCallback.Stub.asInterface(data.readStrongBinder()); this.registerCallback(_arg0); reply.writeNoException(); return true; } case TRANSACTION_unregisterCallback: { data.enforceInterface(DESCRIPTOR); com.ispring.aidldemo.IRemoteInterfaceCallback _arg0; _arg0 = com.ispring.aidldemo.IRemoteInterfaceCallback.Stub.asInterface(data.readStrongBinder()); this.unregisterCallback(_arg0); reply.writeNoException(); return true; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.ispring.aidldemo.IRemoteInterface { 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 getPid() 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); mRemote.transact(Stub.TRANSACTION_getPid, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); }finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public int getData() 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); mRemote.transact(Stub.TRANSACTION_getData, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); }finally { _reply.recycle(); _data.recycle(); } return _result; } @Override public void registerCallback(com.ispring.aidldemo.IRemoteInterfaceCallback cb) throws android.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(); } } @Override public void unregisterCallback(com.ispring.aidldemo.IRemoteInterfaceCallback cb) throws android.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(); } } } static final int TRANSACTION_getPid = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getData = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); static final int TRANSACTION_registerCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2); static final int TRANSACTION_unregisterCallback = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3); } public int getPid() throws android.os.RemoteException; public int getData() throws android.os.RemoteException; public void registerCallback(com.ispring.aidldemo.IRemoteInterfaceCallback cb) throws android.os.RemoteException; public void unregisterCallback(com.ispring.aidldemo.IRemoteInterfaceCallback cb) throws android.os.RemoteException; }
对于上面的代码,我们无需关注具体实现,我们只需要知道以下几点即可:
IRemoteInterface.java中,定义了名为IRemoteInterface的interface,其对应着IRemoteInterface.aidl定义的接口,囊括了IRemoteInterface.aidl中定义的方法。IRemoteInterface继承自android.os.IInterface,如下所示:
public interface IRemoteInterface extends android.os.IInterface
IRemoteInterface接口中有一个静态内部类Stub。Stub是个抽象类,继承自android.os.Binder,且实现了IRemoteInterface接口
public static abstract class Stub extends android.os.Binder implements com.ispring.aidldemo.IRemoteInterface
Sub类中有一个asInterface方法,该方法非常重要,其接收一个android.os.IBinder类型的对象,返回IRemoteInterface类型,其方法签名如下定义:
public static com.ispring.aidldemo.IRemoteInterface asInterface(android.os.IBinder obj)
实现AIDL所定义的接口
我们定义了一个RemoteService类,其继承自Service,该类表示客户端要调用的远程服务,我们在AndroidManifest.xml文件中对该Service进行如下配置:
<service android:name=".RemoteService" android:enabled="true" android:process=":remote" />
我们通过android:process=":remote",让Service运行在一个新的进程中,而不是原有App的进程,其新进程的名字是remote,当然也可以是其他的名称。remote前面的冒号表示该进程被当前的App独享,是该App的私有进程;如果去掉remote前面的冒号,那么则表示该进程是被多个App所共享的。
RemoteService源码如下所示:
package com.ispring.aidldemo; import android.app.Service; import android.content.Intent; import android.os.*; import android.util.Log; import java.util.Random; public class RemoteService extends Service { private Random random = new Random(); //callbacks存储了所有注册过的客户端回调 private final RemoteCallbackList<IRemoteInterfaceCallback> callbacks = new RemoteCallbackList<IRemoteInterfaceCallback>(); private static final int MSG_REPORT_DATA = 0; //该handler用来每隔一秒主动向所有注册过回调的客户端发送信息 private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case MSG_REPORT_DATA: //开始广播,获取客户端的数量 final int n = callbacks.beginBroadcast(); int data = random.nextInt(100); for(int i = 0; i < n; i++){ try{ //遍历客户单回调 IRemoteInterfaceCallback callback = 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 (RemoteException e){ e.printStackTrace(); } } //结束广播 callbacks.finishBroadcast(); //构建新的Message,延迟1秒发送,这样handler每隔一秒都会受到Message Message pendingMsg = obtainMessage(MSG_REPORT_DATA); sendMessageDelayed(pendingMsg, 1000); break; default: super.handleMessage(msg); } } }; //我们要实现 IRemoteInterface.Stub binder = new IRemoteInterface.Stub(){ @Override public int getPid() throws RemoteException { return android.os.Process.myPid(); } @Override public int getData() throws RemoteException { Log.i("DemoLog", "RemoteService: binder -> getData, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName()); return random.nextInt(100); } @Override public void registerCallback(IRemoteInterfaceCallback cb) throws RemoteException { Log.i("DemoLog", "RemoteService: binder -> registerCallback, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName()); if(cb != null){ //注册客户端回调 callbacks.register(cb); } } @Override public void unregisterCallback(IRemoteInterfaceCallback cb) throws RemoteException { Log.i("DemoLog", "RemoteService: binder -> unregisterCallback, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName()); if(cb != null){ //反注册客户端回调 callbacks.unregister(cb); } } }; public RemoteService() { } @Override public void onCreate() { super.onCreate(); handler.sendEmptyMessage(MSG_REPORT_DATA); Log.i("DemoLog", "RemoteService -> onCreate, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName()); } @Override public IBinder onBind(Intent intent) { return binder; } @Override public void onDestroy() { Log.i("DemoLog", "RemoteService -> onDestroy, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName()); //反注册所有的客户端回调,并且不再接收新的客户端回调 callbacks.kill(); //移除pedding message,停止message循环,防止内存泄露 handler.removeMessages(MSG_REPORT_DATA); super.onDestroy(); } }
我们对上述部分代码进行说明:
首先我们通过IRemoteInterface.Stub binder = new IRemoteInterface.Stub()实例化了一个IRemoteInterface.Stub对象。之前我们提到IRemoteInterface.Stub继承自android.os.Binder,所以该实例也就是一个Binder,并且其实现了接口IRemoteInterface定义的抽象方法。 然后在Service的onBind接口中将上述binder对象返回,这样才能让客户端通过bindService方法调用Service。
我们在后面会解释handler、客户端回调相关的代码片段。
AIDL之客户端实现
通常调用Service的客户端是Activity,我在MainActivity中调用RemoteService,其界面如下所示:客户端MainActivity源码如下所示:
package com.ispring.aidldemo; import android.content.ComponentName; import android.content.Intent; import android.content.ServiceConnection; import android.os.*; import android.app.Activity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity implements Button.OnClickListener { private Button btnBindService; private Button btnGetData; private Button btnRegister; private Button btnUnregister; private Button btnUnbindService; private Button btnKillProcess; private TextView textView; private boolean isRegistered = false; private IRemoteInterface remoteInterface; private static final int MSG_GET_DATA = 0; private static final int MSG_DATA_CHANGED = 1; //handler用于在主线程中更新UI private Handler handler = new Handler(){ @Override public void handleMessage(Message msg) { switch (msg.what){ case MSG_GET_DATA: //通过远程服务的getData方法获取数据 Toast.makeText(MainActivity.this, "Data: " + msg.arg1, Toast.LENGTH_LONG).show(); break; case MSG_DATA_CHANGED: //远程服务通过客户端回调向客户端推送数据 textView.setText("Receive data from service: " + msg.arg1); break; default: super.handleMessage(msg); } } }; private ServiceConnection sc = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder binder) { 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 public void onServiceDisconnected(ComponentName name) { 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注册的回调接口 private IRemoteInterfaceCallback callback = new IRemoteInterfaceCallback.Stub(){ @Override public void dataChanged(int data) throws RemoteException { Log.i("DemoLog", "MainActivity: callback -> dataChanged, data: " + data + ", PID=" + + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName()); Message msg = Message.obtain(); msg.what = MSG_DATA_CHANGED; msg.arg1 = data; handler.sendMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { 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 public void onClick(View v) { switch (v.getId()){ case R.id.btnBindService: bindService(); break; case R.id.btnGetData: getData(); break; case R.id.btnRegister: registerCallback(); break; case R.id.btnUnregister: unregisterCallback(); break; case R.id.btnUnbindService: unbindService(); break; case R.id.btnKillProcess: killServiceProcess(); break; } } private void bindService(){ if(remoteInterface != null){ return; } Intent intent = new Intent(MainActivity.this, RemoteService.class); bindService(intent, sc, BIND_AUTO_CREATE); } private void getData(){ if(remoteInterface == null){ return; } try{ Log.i("DemoLog", "MainActivity -> getData"); int data = remoteInterface.getData(); Message msg = Message.obtain(); msg.what = MainActivity.MSG_GET_DATA; msg.arg1 = data; handler.sendMessage(msg); }catch (RemoteException e){ e.printStackTrace(); } } private void registerCallback(){ 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 (RemoteException e){ e.printStackTrace(); } } private void unregisterCallback(){ if(remoteInterface == null || !isRegistered){ return; } try{ Log.i("DemoLog", "MainActivity -> unregisterCallback"); //远程服务反注册客户端回调 remoteInterface.unregisterCallback(callback); isRegistered = false; //更新UI btnRegister.setEnabled(true); btnUnregister.setEnabled(false); }catch (RemoteException e){ e.printStackTrace(); } } private void unbindService(){ 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断开连接"); } private void killServiceProcess(){ if(remoteInterface == null){ return; } try{ Log.i("DemoLog", "MainActivity -> killServiceProcess"); //获取远程服务的进程ID,并杀死远程服务 int pid = remoteInterface.getPid(); android.os.Process.killProcess(pid); remoteInterface = null; //Service进程被杀死后,会触发onServiceDisconnected的执行 }catch (RemoteException e){ 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按钮,可以通过int data = remoteInterface.getData()执行远程服务的getData方法,可以从远程服务中获取最新的数据。我们在完成单击之后过一段时间再次单击getData按钮,这两次单击的输出如下所示:
当客户端调用远程服务AIDL的方法时,这些远程服务的方法是在远程服务的进程中执行的,但是在哪个线程中是不确定的。远程服务内部会维护一个线程池,从线程池中随机取出一个线程执行客户端调用的方法。
当远程服务的getData方法执行完毕后,客户端会得到返回的结果,然后客户端可以根据该值做相应处理,在本例中,我们将得到的data通过Message的形式发送到handler,然后在该handler中更新UI。 registerCallback 通过上面的getData方法可以让客户端从远程服务获取最新的数据,但是很多情况下我们希望远程服务的最新数据发生变化后直接推送给我们客户端,即客户端和远程服务通过观察者模式进行数据更新的通信,此时我们就需要客户端回调。我们在IRemoteInterface中定义了与回调相关的两个方法,registerCallback和unregisterCallback。registerCallback用于向远程服务中注册回调,可以实现Service主动向客户端推送数据。unregisterCallback用于删除注册的回调。二者的方法签名如下所示:
//通过向Service中注册回调,可以实现Service主动向客户端推送数据 void registerCallback(IRemoteInterfaceCallback cb); //删除注册的回调 void unregisterCallback(IRemoteInterfaceCallback cb);
这两个方法都接收一个IRemoteInterfaceCallback参数,IRemoteInterfaceCallback就是一个回调接口,该接口中定义的方法可以被远程服务调用。IRemoteInterfaceCallback也是由AIDL定义的,IRemoteInterfaceCallback.aidl文件如下所示:
package com.ispring.aidldemo; //关键字oneway表示该接口下面的所有方法不会造成客户端阻塞等待服务端方法执行完成 oneway interface IRemoteInterfaceCallback { void dataChanged(int data); }
IRemoteInterfaceCallback.aidl被编译后,会产生IRemoteInterfaceCallback.java文件,其定义了IRemoteInterfaceCallback接口和抽象类IRemoteInterfaceCallback.Stub,IRemoteInterfaceCallback.Stub继承自IBinder,且实现了IRemoteInterfaceCallback接口。通过我们程序中的这两个aidl文件的编译,我们就会发现每个aidl文件编译后,Android Studio都会默认帮我们添加一个其对应的Stub类,我们要和这个Stub类进行操作。
我们在客户单MainActivity通过如下代码实现了IRemoteInterfaceCallback.Stub的一个实例:
//callback为客户端向RemoteService注册的回调接口 private IRemoteInterfaceCallback callback = new IRemoteInterfaceCallback.Stub(){ @Override public void dataChanged(int data) throws RemoteException { Log.i("DemoLog", "MainActivity: callback -> dataChanged, data: " + data + ", PID=" + + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName()); Message msg = Message.obtain(); msg.what = MSG_DATA_CHANGED; msg.arg1 = data; handler.sendMessage(msg); } };
当我们点击registerCallback按钮后,执行了代码remoteInterface.registerCallback(callback),输出如下所示:
我们对以上过程进行一下讲解:
我们将callback作为参数传递给远程服务的registerCallback方法。 RemoteService会执行binder的registerCallback方法,代码如下所示:
@Override public void registerCallback(IRemoteInterfaceCallback cb) throws RemoteException { Log.i("DemoLog", "RemoteService: binder -> registerCallback, PID=" + android.os.Process.myPid() + ", Thread=" + Thread.currentThread().getName()); if(cb != null){ //注册客户端回调 callbacks.register(cb); } }
在RemoteService中,我们定义了一个RemoteCallbackList<IRemoteInterfaceCallback>类型的callbacks参数,用它存储所有注册过的客户端回调。通过callbacks.register(cb),将客户端传递过来的回调参数注册到callbacks中。 我们在RemoteService中定义了一个handler,每隔一秒发送一个Message,模拟数据发生了变化,然后向所有的客户端主动发送更新后的数据。handler中主要的相关代码如下所示:
//开始广播,获取客户端的数量 final int n = callbacks.beginBroadcast(); int data = random.nextInt(100); for(int i = 0; i < n; i++){ try{ //遍历客户单回调 IRemoteInterfaceCallback callback = 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 (RemoteException e){ e.printStackTrace(); } } //结束广播 callbacks.finishBroadcast(); //构建新的Message,延迟1秒发送,这样handler每隔一秒都会受到Message Message pendingMsg = 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:remote I/DemoLog: RemoteService -> onDestroy, PID=6106, Thread=main需要注意的是,单纯调用unbindService方法没有触发ServiceConnection的onServiceDisconnected()方法的执行。 Kill Service Process 在客户端与远程服务处于绑定的情况下,点击Kill Service Process按钮,可以杀死远程服务的进程,控制台输出如下所示:
首先通过我们在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的,这样远程服务调用回调接口中的方法时不会阻塞远程服务后面代码的执行。 收藏的用户(0) X
正在加载信息~
推荐阅读
最新回复 (0)
站点信息
- 文章2300
- 用户1336
- 访客10857300
每日一句
Challenges spark life; conquering them gives purpose.
挑战点亮生活,征服赋予意义。
挑战点亮生活,征服赋予意义。
语法错误: 意外的令牌“标识符”
全面理解Gradle - 定义Task
Motrix全能下载工具 (支持 BT / 磁力链 / 百度网盘)
谷歌Pixel正在开始起飞?
获取ElementUI Table排序后的数据
Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is
亲测!虚拟机VirtualBox安装MAC OS 10.12图文教程
华为手机app闪退重启界面清空log日志问题
android ndk开发之asm/page.h: not found
手机屏幕碎了怎么备份操作?
免ROOT实现模拟点击任意位置
新手必看修改DSDT教程
thinkpad t470p装黑苹果系统10.13.2
新会员