第一部分 AIDL的概念以及使用步骤
Android系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。
为了使其他的应用程序也可以访问本应用程序提供的服务,Android系统采用了远程过程调用(Remote Procedure Call,RPC)方式来实现。与很多其他的基于RPC的解决方案一样,Android使用一种接口定义语言(Interface Definition Language,IDL)来公开服务的接口。这种可以跨进程访问的服务称为AIDL(Android Interface Definition Language)服务。所以AIDL服务专门是用来解决进程间通信的(IPC)。
第一步:定义接口语言,在Android工程的Java包目录中建立一个扩展名为aidl的文件,与java目录同级
// IMyAidlInterface.aidl package com.example.nft.myapplication; // Declare any non-default types here with import statements interface IMyAidlInterface { String getInfor(String s); int add(int a,int b); }
点击Rebuild project按钮就可以在app/generated/source/aidl/debug/aidl里面发现由IMyAidlInterface.aidl文件生成的java文件。
第二步:新建service子类,并利用Binder来实现由aidl定义的方法
public class RemoteService extends Service { private IBinder binder = new IMyAidlInterface.Stub() { @Override public String getInfor(String s) throws RemoteException { Log.i("niuniu ", " 收到了客户端的请求 " +s); return "我是远程Service返回的数值"; } @Override public int add(int a, int b) throws RemoteException { Log.i("niuniu ", " 收到了客户端的数据 a = " + a +" b = " +b); return a+b; } }; @Override public void onCreate() { super.onCreate(); Log.i("niuniu", " RemoteService oncreate"); } @Nullable @Override public IBinder onBind(Intent intent) { return binder; } }
第三步:在AndroidManifest.xml文件中配置RemoteService
<service android:name=".RemoteService" > <intent-filter> <action android:name="com.nft.application"> </action> </intent-filter> </service>
通过AIDL来建立远程服务的通信机制就到此完成了。接下来,以activity作为客户端的demo,与远程服务进行IPC
首先新建一个project,同样的新建AIDL 文件夹,并把服务端的aidl文件copy到客户端,包括包名,要保持一致,再 rebuild project
其次是在该应用下的MainActivity中绑定远程,并在onServiceConnected回调方法中调用由远程服务实现aidl定义的方法
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.activity_main); MyCon myCon = new MyCon(); Intent intent = new Intent(); intent.setPackage("com.example.nft.myapplication"); intent.setAction("com.nft.application"); bindService(intent,myCon, Context.BIND_AUTO_CREATE); } public class MyCon implements ServiceConnection { private IMyAidlInterface iService; @Override public void onServiceConnected(ComponentName name, IBinder service) { Log.i("niuniu"," service 连接成功"); iService = IMyAidlInterface.Stub.asInterface(service); try { String s = iService.getInfor(" 当前应用传进去的参数"); Log.i("niuniu", " 从远程服务返回的数值是: "+s); int sum = iService.add(3,2); Log.i("niuniu", " 从远程服务返回的和: "+sum); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } } }
试运行的结果
03-01 15:51:25.313 13925 13925 I niuniu : service 连接成功 03-01 15:51:25.315 13604 13642 I niuniu : 收到了客户端的请求 当前应用传进去的参数 03-01 15:51:25.315 13925 13925 I niuniu : 从远程服务返回的数值是: 我是远程Service返回的数值 03-01 15:51:25.315 13604 13627 I niuniu : 收到了客户端的数据 a = 3 b = 2 03-01 15:51:25.315 13925 13925 I niuniu : 从远程服务返回的和: 5
当客户端连接service成功后,就将 "当前应用传进去的参数"传递给远程服务,远程服务收到请求就有log输出,并return相应的结果,客户端通过调用其方法可以得到远程方法的返回值。
结论:这就是远程service利用aidl机制实现与客户端activity之间的数据通信,aidl作为公共服务接口,在远程service中实现,在activity中通过绑定远程服务service来实现参数的传递以及返回值的获取。
第二部分:AIDL的原理
不管是在远程服务中还是在本地客户端中,建立好aidl文件后,都需要rebuild project ,此时会在app/generated/source/aidl/debug/aidl里面看到相应的java文件了
package com.example.nft.myapplication; // Declare any non-default types here with import statements public interface IMyAidlInterface extends android.os.IInterface { /** * Local-side IPC implementation stub class. */ public static abstract class Stub extends android.os.Binder implements com.example.nft.myapplication.IMyAidlInterface { private static final java.lang.String DESCRIPTOR = "com.example.nft.myapplication.IMyAidlInterface"; /** * Construct the stub at attach it to the interface. */ public Stub() { this.attachInterface(this, DESCRIPTOR); } /** * Cast an IBinder object into an com.example.nft.myapplication.IMyAidlInterface interface, * generating a proxy if needed. */ public static com.example.nft.myapplication.IMyAidlInterface asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } //检查obj是不是当前进程中的binder android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.example.nft.myapplication.IMyAidlInterface))) { return ((com.example.nft.myapplication.IMyAidlInterface) iin); } return new com.example.nft.myapplication.IMyAidlInterface.Stub.Proxy(obj); } @Override public android.os.IBinder asBinder() { return this; } /**参数说明:code,区分不同类型的接口,比如,在例子中Stub.TRANSACTION_getInfor data:从客户端传过来的数据,包含方法参数以及类的描述, reply:远程服务处理后的数据,返回给客户端,包括是否发生了异常以及返回的结果 flags:该方法是否有返回值,0表示有*/ @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_getInfor: { //当客户端将数据传递成功后,会回调该方法,并将类名,以及传递进来的方法参数一并取出来,供RemonteService中binder重写的getinfor使用 data.enforceInterface(DESCRIPTOR); java.lang.String _arg0; _arg0 = data.readString(); java.lang.String _result = this.getInfor(_arg0); //将远程服务中的结果,写入repy中,发送给客户端proxy reply.writeNoException(); reply.writeString(_result); 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; } } return super.onTransact(code, data, reply, flags); } private static class Proxy implements com.example.nft.myapplication.IMyAidlInterface { 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 java.lang.String getInfor(java.lang.String s) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.lang.String _result; try { //将客户端传进来的方法参数,以及类名,一并写入_data中,并通过transact传递给远程服务 _data.writeInterfaceToken(DESCRIPTOR); _data.writeString(s); mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0); //远程服务的onTransact中处理完数据后,返回true之后,proxy就可以将远程服务的数据读出来 _reply.readException(); _result = _reply.readString(); } finally { _reply.recycle(); _data.recycle(); } return _result; } @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); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; } } static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public java.lang.String getInfor(java.lang.String s) throws android.os.RemoteException; public int add(int a, int b) throws android.os.RemoteException; }自动生成的这个java文件,其重点内容是一个静态抽象stub类,该类是实现了 IMyInterface定义的方法并继承了Binder的类,
1 在远程服务中 new IMyInterface.Stub()就会将并把它向上转型成了IBinder,最后在onBind方法中返回回去。并在IMyInterface.Stub()的内部我们重写getInfor(String s) ,add(int a,int b)方法。
2 在本地客户端中 new ServiceConnection并实现onServiceConnected、onServiceDisconnected。在onServiceConnected中通过IMyInterface.Stub.asInterface(service)把传来的IBinder转换成了IMyInterface。然后调用getInfor,add(int a,int b)方法,传递了参数和获取从RemouteService返回的结果,并且打印了Log。
3 当本地客户端成功绑定RemouteService时,会得到一个由new IMyInterface.Stub()转型的binder,并在onServiceConnected回调中,将这个binder作为参数传递给IMyInterface.Stub.asInterface(service)就可以得到IMyInterface了,然后利用IMyInterface,来婉转的调用已经由new IMyInterface.Stub()重写的getInfor(String s) ,add(int a,int b)方法。
4asInterface这个静态方法中,会对客户端传进来的binder进行一系列的判断,首先判断IBinder是不是null,如果为null就返回一个null;接着就判断传进来的IBinder是不是就在当前进程里面,如果是的话就直接返回IMyInterface,不是的话就返回IMyInterface.Stub.Proxy(obj)。直接返回的IMyInterface是实现了定义的接口方法getInfor的。因为在IMyInterface.Stub中所实现的。当然如果是在同一进程中,那么我们调用IMyInterface的方法时就是在本地调用方法,直接调用就可以了。如果没在同一进程,就会返回IMyInterface.Stub.Proxy(obj):
5 在Proxy中,首先把远程服务RemonteService连接成功返回的IBinder它的内部变量mRemote,。然后,当我们调用IMyInterface的方法的时候,其实就是调用实现IMyInterface接口的Proxy的类方法。
所以当客户端调用IMyInterface.getInfor(String s) ,其实就是调用Proxy中的getInfor,它先获取了两个Parcel对象 _data、_reply,一个是传送数据的,另一个是接受返回数据的。接着,向_data中写入了DESCRIPTOR(也就是这个类的全名),再写入传递进来的s参数。然后利用转换成本地变量binder的mRemote,将数据传递给远程服务,
mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
这里调用了IBinder的transact方法,把数据传给远端的服务器。当数据成功传送到远程服务时,远程服务中的Stub,作为Binder子类,就会回调onTransact(),onTransact方法是在Stub的内部实现的.
6 在远程服务中的stub如果调用了onTransact就表示有数据传来,首先就会通过swicth判断是哪个方法,然后取出方法参数,调用本地实现的方法获取返回值,也就是调用new IMyInterface.Stub()重写的getInfor(String s)方法,并将返回值写入reply。最后,返回true,才会把数据发送给客户端,相反,返回false就不会把结果返回给Activity了。这里也就是说,只有返回true,Proxy中才能接受从远程服务传回的数据,也就是说可以获取到reply.readXXX的值了。
结论:
自动生成的Stub这个静态抽象方法中有:
1自身是实现IMyInterface方法的Binder子类,可以在同一进程中重写接口方法,供本地服务调用
2asInterface的静态方法,是返回IMyInterface对象的,供客户端调用相应的接口方法
3Proxy的内部类,是实现IMyInterface方法的一个类,供客户端调用远程服务实现的接口方法,将远程服务的binder本地化,并通过transact将参数传送给远程服务。
4onTransact()回调,是stub作为binder子类的一个基本回调,供客户端传递数据成功后,使用远程服务的方法获取结果后,将结果发送给客户端
5 对于远程服务,通过new IMyInterface.Stub()实现了aidl定义的方法,并返回binder来供客户端间接的调用已实现的接口,对于客户端,通过IMyInterface.Stub.asInterface(service),利用远程服务返回的binder,获取到IMyInterface对象,并通过调用已实现的接口来获取远程服务发送的结果
第三部分 远程service的另一种写法
因为new IMyInterface.Stub()是返回binder,让service返回给客户端,所以也可以自己定义binder类,返回给客户端,主要自己定义的binder也是继承了binder并实现IMyInterface方法即可
@Nullable @Override public IBinder onBind(Intent intent) { return new MyBinder(); } private class MyBinder extends IMyAidlInterface.Stub{ @Override public String getInfor(String s) throws RemoteException { Log.i("niuniu ", " 收到了客户端的请求"); return "我是远程服务返回的字串"; } @Override public int add(int a, int b) throws RemoteException { Log.i("niuniu ", " 收到了客户端的数据 a = " + a +" b = " +b); return a+b; } }