上回书说到,如何使用AIDL进行线程进通信
这次我们来研究一下AIDL线程间通信原理。
首先我们需要知道一下什么是Binder机制,具体什么是Binder机制推荐多查一查其他博客,由于太过于偏向底层,稍微做一些了解即可。
还是先上代码,首先看一下我们自己定义的AIDL接口
注意,一下代码均是在Android-29(Android 10.0)版本上的
interface IMyAidlInterface {
void sendMessage(in String msg);
String getMessage();
}
定义完成接口之后编辑器会自动为我们生成IMyAidlInterface.java这个类
我们使用时会在客户端绑定服务进程,在onServiceConnected中获得到IMyAidlInterface的实例对象,下面我们一起来看看代码。有关于如何绑定service以及如何拿到的实例对象请参考我的另一篇文章。
Android AMS源码分析之结合AIDL分析bindService
先看下部分关键代码及注释内容
//定义一个这个东西
private IMyAidlInterface myAidl;
public void onServiceConnected(ComponentName name, IBinder service) {
//在这里拿到它的实例
/**
*在这里我们记住传入的参数“service” 是一个Ibinder对象
*/
myAidl = IMyAidlInterface.Stub.asInterface(service);
}
然后在IMyAidlInterface中初始化
最终return的是
return new com.tiancong.myaidl1.IMyAidlInterface.Stub.Proxy(obj);
在这里我们先看一下他们的层级关系
在Proxy只有这一个构造方法,所以记住这个mRemote就是我们传入的参数
private static class Proxy implements com.tiancong.myaidl1.IMyAidlInterface
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
}
......
}
我们可以看到 Proxy 也是实现了IMyAidlInterface接口的,所以我们客户端调用的接口中的方法的最终实现就在在这里实现的。
我们可以清楚的看到 例如sendmessage
@Override public void sendMessage(java.lang.String msg) 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.writeString(msg);
boolean _status = mRemote.transact(Stub.TRANSACTION_sendMessage, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().sendMessage(msg);
return;
}
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
在这里我们会发现我们sendmessage方法的参数msg被一个_data的成员打包进去了,然后使用了
mRemote.transact(…)的方法,我们上文提到mRemote是就是我们的Ibinder,IBinder又是一个接口,所以我们就要去找实现IBinder接口的Binder类里面去看transact方法的具体实现,接下来的跳转就很简单了,通过Binder的transact调用data.setDataPosition()方法,最终到nativeSetDataPosition(mNativePtr, pos)方法。我们知道native方法是本地方法,都是C、C++来实现的,在这里我们不做深入了解,最终的结果就是把数据发送了出去
补充transact下面代码
public final boolean transact(int code, @NonNull Parcel data, @Nullable 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);这行代码,待会要考。
接下来我们来看服务端代码,看看服务的是怎么完成接受数据并返回的
绑定服务的代码就不看了,我们先看下在服务端是如何绑定的
@Override
public IBinder onBind(Intent intent) {
Log.d(TAG, "onBind: ");
return iBinder;
}
private IBinder iBinder = new IMyAidlInterface.Stub() {
@Override
public void sendMessage(String msg) throws RemoteException {
message = " "+msg +" " + "被service处理了";
Log.d(TAG, "sendMessage: "+msg);
}
@Override
public String getMessage() throws RemoteException {
Log.d(TAG, "getMessage: "+message);
return message;
}
};
我们在在服务开启执行onBind的时候返回了iBinder,我们可以清楚的看到这个iBinder就是我们IMyAidlInterface.stub()的一个实例对象。我们看下这个东西。
public static abstract class Stub extends android.os.Binder implements com.tiancong.myaidl1.IMyAidlInterface
我们看到,这个stub继承Binder类,在里面又重写了上面的提到的onTransact方法,再加上一些底层不了描述的逻辑,完了了最终在客户端通过Transact发送,在服务的onTransact中接受,例如我们sendmesseg方法最终会在服务的执行。关于这里面的具体细节我们可以参考一下下面这张图片,只是作为了解一下就可以了
case TRANSACTION_sendMessage:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.sendMessage(_arg0);
reply.writeNoException();
return true;
}
可以看到this.sendMessage(_arg0);我们在stub类里面没有找到sendmessage的实现,所以我们的最终调用的sendmessage就是我们在服务中重新的sendmessage方法。这样就完成了一次完整的进程间通信操作。