以前写过一个service的远程快进程调用,https://blog.csdn.net/Deaht_Huimie/article/details/52693191, 这一篇文章介绍的比较粗浅。
如果是两个app之间通讯,则两个app中的aidl需要一样,包括包名,通过绑定service和 ServiceConnection 来实现通讯,用法和一个app内跨进程通讯一样,这方面网上也有许多介绍。我们使用android studio 时,在 src 目录下的 main 目录中,创建 aidl 文件夹,然后创建自己所需要的文件夹名字,创建 .aidl 文件,例如 创建一个 ServiceAidlInterface.aidl
package duan.com.notictiontest;
interface ServiceAidlInterface {
void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
double aDouble, String aString);
int add(int num1,int num2);
}
.aidl 是接口格式,add() 方法是我们自定义的通讯方法,basicTypes()方法是默认生成的,为了告诉我们可以传递哪些基本类型。如果我们要传递对象的话,对象需要序列化,实现Parcelable 接口;写好后,gradle 一下,然后系统就会自动生成对应的 ServiceAidlInterface.java 文件,我们可以看一下
package duan.com.notictiontest;
// Declare any non-default types here with import statements
public interface ServiceAidlInterface extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements duan.com.notictiontest.ServiceAidlInterface
{
private static final java.lang.String DESCRIPTOR = "duan.com.notictiontest.ServiceAidlInterface";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an duan.com.notictiontest.ServiceAidlInterface interface,
* generating a proxy if needed.
*/
public static duan.com.notictiontest.ServiceAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof duan.com.notictiontest.ServiceAidlInterface))) {
return ((duan.com.notictiontest.ServiceAidlInterface)iin);
}
return new duan.com.notictiontest.ServiceAidlInterface.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_basicTypes:
{
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
long _arg1;
_arg1 = data.readLong();
boolean _arg2;
_arg2 = (0!=data.readInt());
float _arg3;
_arg3 = data.readFloat();
double _arg4;
_arg4 = data.readDouble();
java.lang.String _arg5;
_arg5 = data.readString();
this.basicTypes(_arg0, _arg1, _arg2, _arg3, _arg4, _arg5);
reply.writeNoException();
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 duan.com.notictiontest.ServiceAidlInterface
{
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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public int add(int num1, int num2) 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(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int add(int num1, int num2) throws android.os.RemoteException;
}
我们可以看到, ServiceAidlInterface 也是个接口,实现了 IInterface 接口,
public interface IInterface
{
public IBinder asBinder();
}
Stub 是个内部类,如果我们把它提出来,则 ServiceAidlInterface 的代码为
public interface ServiceAidlInterface extends android.os.IInterface
{
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) throws android.os.RemoteException;
public int add(int num1, int num2) throws android.os.RemoteException;
}
aidl 是对 Binder 的封装,我们只要按照格式写,系统会自动帮我们实现其他的代码,我们直接使用就可以了,避免自己做大量的代码工作。看上述代码,ServiceAidlInterface 中默认的只有一个 add() 方法,此时经过系统的生成,自动实现了 IInterface ,并生成内部类 Stub,跨进程主要就是依靠 IInterface,按照一定的协议进行数据交互。看看 Stub 都是什么:Binder 机制中,需要 binder和IInterface,同时需要把具体的操作给抽象出去,Binder 机制只是个框架及协议。 继续往下看
Stub 是个抽象类,继承了 Binder ,实现了 ServiceAidlInterface 接口,
private static final java.lang.String DESCRIPTOR = "duan.com.notictiontest.ServiceAidlInterface";
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
DESCRIPTOR 这个属性,是由 aidl 的包名和类名组成的,算是一个唯一的标识; 在 Stub 的构造函数中, 会把自身当做 IInterface 参数,和 DESCRIPTOR 一块传给 attachInterface方法,我们看一下 Binder 中的这两个方法
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
这个很明显了,创建 Stub 也就是 Binder 时,会给它打印上一个唯一的标记,并把 IInterface 赋值给Binder中的一个成员变量,然后通过 queryLocalInterface() 方法来查询出 IInterface 是否是我们需要的对象
public static duan.com.notictiontest.ServiceAidlInterface asInterface(android.os.IBinder obj){
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof duan.com.notictiontest.ServiceAidlInterface))) {
return ((duan.com.notictiontest.ServiceAidlInterface)iin);
}
return new duan.com.notictiontest.ServiceAidlInterface.Stub.Proxy(obj);
}
这里的 asInterface 方法,就是我们客户端定义 ServiceConnection 中 onServiceConnected(ComponentName componentName, IBinder iBinder) 方法中获取 ServiceAidlInterface adil 的调用的方法 : aidl = ServiceAidlInterface.Stub.asInterface(iBinder); 我们看 asInterface() 中,通过 obj.queryLocalInterface(DESCRIPTOR) 来获取所需要的对象,如果是,则返回,否则会创建一个 Proxy 对象,这里明显是用到了代理模式。
public android.os.IBinder asBinder()
{
return this;
}
这个方法是重写了 IInterface 接口中的方法,返回的对象就是自己。
onTransact()方法暂且跳过去,先不分析,我们看看代理类 Proxy
private static class Proxy implements duan.com.notictiontest.ServiceAidlInterface
{
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 void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, java.lang.String aString) 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.writeInt(anInt);
_data.writeLong(aLong);
_data.writeInt(((aBoolean)?(1):(0)));
_data.writeFloat(aFloat);
_data.writeDouble(aDouble);
_data.writeString(aString);
mRemote.transact(Stub.TRANSACTION_basicTypes, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public int add(int num1, int num2) 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(num1);
_data.writeInt(num2);
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
static final int TRANSACTION_basicTypes = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
Proxy 实现了 ServiceAidlInterface 接口,同时接收了上面 asInterface(android.os.IBinder obj) 中的 obj,Proxy中定义了 IBinder mRemote 来接收这个参数,我们发现,里面多了两个自定义的静态常量值 TRANSACTION_basicTypes 和 TRANSACTION_add,通过名字可以看出,是用 TRANSACTION_ 拼接上我们自定义接口中的方法名,合成的常量名,它们对应的值是在 IBinder.FIRST_CALL_TRANSACTION = 0x00000001 的基础上依次累加,这个也是作为方法的唯一识别码。我们主要看 add() 方法,一开始创建两个 Parcel 对象,一个是用来写入数据,传递给服务端,一个是用来接收服务端返回的数据,然后返回给调用的地方; _data 在写入数据前,会 _data.writeInterfaceToken(DESCRIPTOR);把唯一的标识写入进去,然后是
_data.writeInt(num1); _data.writeInt(num2); 写入数据, mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); 开启了远程调用,获取数据,_reply.readException();是进行异常校验,是否异常,如果有,会抛出异常;_result = _reply.readInt(); 是读取获取的数据,然后及时释放资源了。可能会有疑惑,远程调用 mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); 是怎么获取数据的? 我们继续分析,Binder 实现了 IBinder 接口,看看 Binder 中 transact() 方法
public final boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException {
if (false) Log.v("Binder", "Transact: " + code + " to " + this);
if (data != null) {
data.setDataPosition(0);
}
boolean r = onTransact(code, data, reply, flags);
if (reply != null) {
reply.setDataPosition(0);
}
return r;
}
发现,最终会调用 boolean r = onTransact(code, data, reply, flags); 方法,如果 r 为true,则数据通讯成功,否则失败;我们看看 onTransact() 方法是什么,Binder 中的方法有些代码,浙西都是底层交互的,可以不用看,我们重点关注生成的 ServiceAidlInterface 中的代码,我们发现 Proxy 中没有这个方法,因为它是个代理类,况且是 mRemote 调用了 transact() 方法,我们看看 Stub 中的方法
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException{
switch (code){
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);
}
这是简化后的代码, mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); 这个是调用的代码,那么 code 就是 Stub.TRANSACTION_add, data 就是 _data,reply 就是 reply, data.enforceInterface(DESCRIPTOR); 对应 _data.writeInterfaceToken(DESCRIPTOR); 这一步应该校验, int _arg0 = data.readInt();int _arg1 = data.readInt();这一步就是读出传入的数据,然后调用 int _result = this.add(_arg0, _arg1);获取到值,reply.writeInt(_result);把值给传进去,对应着 Proxy 中 _result = _reply.readInt();取出数据;这里的 int _result = this.add(_arg0, _arg1) 方法,add() 是抽象方法,需要服务端自定义对象继承 Stub 类,来实现这个方法,并把它传给Service 中的 onBinder().
服务端 :
public class IRemoteService extends Service {
public IRemoteService() {
}
@Override
public IBinder onBind(Intent intent) {
return new ServiceAidlServe();
// return iBinder;
}
private IBinder iBinder = new ServiceAidlInterface.Stub(){
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int add(int num1, int num2) throws RemoteException {
Log.i("AIDL","收到了请求,參數1"+num1+"參數2"+num2);
return num1+num2;
}
};
static class ServiceAidlServe extends ServiceAidlInterface.Stub{
@Override
public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
}
@Override
public int add(int num1, int num2) throws RemoteException {
Log.i("AIDL","ServiceAidlServe收到了请求,參數1"+num1+"參數2"+num2);
return num1+num2;
}
}
}
客户端:
Activity 中代码:
private ServiceAidlInterface aidl;
private ServiceConnection conn = new ServiceConnection() {
//连接上
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
//拿到远程服务
aidl = ServiceAidlInterface.Stub.asInterface(iBinder);
}
//断开时
@Override
public void onServiceDisconnected(ComponentName componentName) {
//回收
aidl = null;
}
};
private void bindServices() {
//获取服务端
Intent intent = new Intent();
//5.0之后的改变
intent.setComponent(new ComponentName("duan.com.notictiontest","duan.com.notictiontest.IRemoteService"));
//绑定服务
bindService(intent,conn, Context.BIND_AUTO_CREATE);
}
private void testSum() {
int num1 = 13;
int num2 = 25;
try {
//结果
int res = aidl.add(num1,num2);
mTvSum.setText("结果:"+res);
} catch (Exception e) {
e.printStackTrace();
}
}
aidl 是封装好的模版,方便我们使用;掌握了原理后,我们也可以不用系统的aidl,完全自定义来实现此功能,但一般不建议这么干。
对于Stub的asInterface(Binder obj), 它可以返回 Stub 或 Stub.Proxy,如果客户端和服务端在同一个进程下,那么asInterface()返回的是 Stub,否则返回 Stub.Proxy。Stub 和 Stub.Proxy 的区别:(1)如果在同一个进程下的话,那么asInterface()将返回服务端的 Stub 对象本身,此时不需要跨进称通信,那么直接调用 Stub 对象的接口就可以了,返回的实现类就是服务端的 Stub 实现;(2)如果是不同进程,asInterface()返回是 Stub.Proxy 对象,该对象持有着远程的 Binder 引用,所以如果调用 Stub.Proxy 的接口的话,它们都将是IPC调用,会通过调用 transact 方法去与服务端通信。