AI智能
改变未来

Android之AIDL使用binder进程间通信原理

上回书说到,如何使用AIDL进行线程进通信
这次我们来研究一下AIDL线程间通信原理。

Binder机制

首先我们需要知道一下什么是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);这行代码,待会要考。

接下来我们来看服务端代码,看看服务的是怎么完成接受数据并返回的
绑定服务的代码就不看了,我们先看下在服务端是如何绑定的

@Overridepublic IBinder onBind(Intent intent) {Log.d(TAG, \"onBind: \");return iBinder;}private IBinder iBinder = new IMyAidlInterface.Stub() {@Overridepublic void sendMessage(String msg) throws RemoteException {message = \"  \"+msg +\"  \" + \"被service处理了\";Log.d(TAG, \"sendMessage: \"+msg);}@Overridepublic 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方法。这样就完成了一次完整的进程间通信操作。

如果对Android进程间通信还有什么疑问,欢迎关注我的微信公众号,我们来一起进行讨论。

赞(0) 打赏
未经允许不得转载:爱站程序员基地 » Android之AIDL使用binder进程间通信原理