第一部分 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;
}
}