Android 跨进程通信-从源码分析AIDL跨进程通信实现

Posted 好人静

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 跨进程通信-从源码分析AIDL跨进程通信实现相关的知识,希望对你有一定的参考价值。

前言

Android 跨进程通信-(八)AIDL中的代理模式之源码分析 中其实主要还是在总结AIDL中的代理模式的体现,也提到了Client和Server之所以能够进行交互数据,最主要的原因就是依赖Binder驱动,这次就是从Client和Server传递数据的过程在总结下流程,并且就是一一解决之前遇到的一些问题。

简单的拿应用层的AIDL服务来看下这个源码的调用过程,大体分为:

  • 1.bindService()将Client和Server建立关系
    • (1)Server通过onBind()将Stub返回给Client(Binder驱动在Server是Binder)
    • (2)Client在onServiceConnected()中得到BinderProxy(Binder驱动在Client就是BinderProxy)
  • 2.Client通过BinderProxy来调用接口方法
  • 3.驱动通知Server来调用对应的接口方法

一 Client和Server建立关系

在前面的Android 跨进程通信-(四)Binder机制之Server提到Client进程调用到bindService()来创建Service的时候,会最终调用到AMS中的startProcessLocked()来通知Zygote进程来创建Service进程,当创建Service进程成功并完成初始化之后,就会加载入口类ActivityThread。

1.bindService()

在Activity中通过bindService()来将Client和Server建立关系,会调用到ContextWrapper中的bindService()。Activity、Service直接或间接继承了ContextWrapper。

    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        return mBase.bindService(service, conn, flags);
    }

这里传入了一个ServiceConnection,用于当Client和Server建立连接的时候,通过onServiceConnected()返回给Client一个IBinder对象,该对象其实是一个BinderProxy,是Server的Binder驱动的本地代理。 

遗留问题:ContextWrapper和ContextImpl这里是代理模式还是装饰模式呢?从命名上感觉是装饰者模式。后面需要研究下这个地方。

该bind功能的具体实现就是ContextImpl ,该文件位于/frameworks/base/core/java/android/app/ContextImpl.java,其中部分逻辑代码如下:

   @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
                Process.myUserHandle());
    }
  private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
            handler, UserHandle user) {
        // Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
        IServiceConnection sd;
    ........
        if (mPackageInfo != null) {
        //从ServiceConnection获得一个IServiceConnection
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
        } 
        ........
        try {
            IBinder token = getActivityToken();
        //调用AMS中的bindService()来创建出Service进程以及完成绑定
            int res = ActivityManager.getService().bindService(
                mMainThread.getApplicationThread(), getActivityToken(), service,
                service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, getOpPackageName(), user.getIdentifier());
            ........   
    }

该方法中首先要将ServiceConnection转换成IServiceConnection。ServiceConnection并不支持跨进程,而IServiceConnection可以跨进程通信。ServiceDispatcher就是负责维护ServiceConnection和IServiceConnection之间的关系。

接下来就是通过AMS的bindService来完成这个绑定过程。这里在Android 跨进程通信-(四)Binder机制之Server中已经分析过了,这个时候AMS会通知Zygote进程来fork出该Service进程,然后加载ActivityThread类,通过消息处理机制先后处理CREATE_SERVICE、BIND_SERVICE对应的事件。

2.ActivityThread中的BIND_SERVICE事件

在ActivityThread中处理BIND_SERVICE事件的时候,会完成两个事情:

    private void handleBindService(BindServiceData data) {
            Service s = mServices.get(data.token);
     .........
                    if (!data.rebind) {
                        IBinder binder = s.onBind(data.intent);
                        ActivityManager.getService().publishService(
                                data.token, data.intent, binder);
  • 第一个:就是调用到service中的onBind()

在应用层的Serivice中,我们会将Stub的继承类的实例通过onBind()返回给Client。在new一个Stub的实例的时候,其中Stub的构造函数有一些关键代码:

    public Stub()
    {
      this.attachInterface(this, DESCRIPTOR);
    }

 最终在Server进程中调用到Binder的attachInterface(),那么该Stub的继承类保存到Server进程中,当Client通过onBind()返回的IBinder对象queryLocalInterface()的时候,就会得到该Stub的继承类的实例。

  • 第二个:调用到AMS的publishService()回调到ServiceConnection的onServiceConnected()

在BIND_SERVICE事件最终会调用到/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java的publishServiceLocked(),代码如下:

 void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
   ........
                            try {
                                c.conn.connected(r.name, service, false);
                            } 
    ........
    }

此时通过IServiceConnection来调用到Client的onServiceConnected()。

遗留问题:这里理解的不是很透彻,后面在细细研究下!

二 Client调用接口方法

1.应用层调用逻辑

Client在调用接口方法时,例如

        public void onServiceConnected(ComponentName name, IBinder service) {
            baiduPushMessageService = IBaiduPushMessageService.Stub.asInterface(service);
            baiduPushMessageService.setBaiduMessageTitle("标题");
        ......
        }

2.aidl编译的java文件调用逻辑

asInterface()在跨进程通信baiduPushMessageService对应的就是Stub.Proxy的实例,所以最终会调用到Stub.Proxy的setBaiduMessageTitle(),如

      @Override public void setBaiduMessageTitle(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_setBaiduMessageTitle, _data, _reply, 0);
          .........
      }

而我们传入的mRemote即为BinderProxy。所以这里Client通过BinderProxy.transact()将序列化的数据打包发给向Binder驱动,其中transact()的对应字段如下:

    /**
     *
     * @param code:要执行动作的标示
     * @param data:从Client传到Server序列化的数据,不能为空
     * @param reply:从服务器返回的序列化的数据,可能为空
     * @param flags:0表示阻塞等待该方法调用结束;1表示执行该方法后立即执行
     * @return
     */
    public boolean transact(int code,  Parcel data, Parcel reply, int flags);

3.Framework层BinderProxy的逻辑

由于IBaiduPushMessageService.Stub.Proxy子类中没有复写transact(),所以调用到Binder.Proxy的transact(),如下:


    public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
       .......
        try {
            return transactNative(code, data, reply, flags);
        }
     .......
    }

然后通过JNI调用到C++的具体实现代码/frameworks/base/core/jni/android_util_Binder.cpp的android_os_BinderProxy_transact(),如下:

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
        jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
  .........
    //将java层的BinderProxy转换成Native的BpBinder
    IBinder* target = (IBinder*)
        env->GetLongField(obj, gBinderProxyOffsets.mObject);
  .........
    //调用到BpBinder的transact
    status_t err = target->transact(code, *data, reply, flags);
  .........
}

在这方法中就是将BinderProxy转换成Native的BpBinder,然后调用BpBinder::transact,这样调用逻辑就到了Native的BpBinder。

4.Native层BpBinder的逻辑

通过系统调用,直接调用到Native层封装的BpBinder::transact,该文件位于/frameworks/native/libs/binder/BpBinder.cpp,是对Binder驱动在Native的封装。


status_t BpBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
    // Once a binder has died, it will never come back to life.
    if (mAlive) {
        status_t status = IPCThreadState::self()->transact(
            mHandle, code, data, reply, flags);
        if (status == DEAD_OBJECT) mAlive = 0;
        return status;
    }

    return DEAD_OBJECT;
}

然后调用到IPCThreadState,该类具体位于/frameworks/native/libs/binder/IPCThreadState.cpp,而IPCThreadState是Native层具体的和Binder驱动进程交互的操作类,完成具体的进程的IPC数据读写,最终通过IPCThreadState::talkWithDriver()通过ioctl()向Binder驱动发送写命令(ProcessState负责打开Binder驱动,并做好内存映射,该过程发生在创建进程阶段)。这样就完成Client进程将要传递的数据发送到了Binder驱动中。

三 Binder驱动通知Server

1.Native层的BBinder的逻辑

Binder驱动会通知Server进程开始读取Client进程传过来的信息。与Client从应用层传到Kernel的顺序正好相反,Binder驱动从kernel一直传到Server的Native的IPCThreadState::talkWithDriver(),在IPCThreadState::waitForResponse就会接收到该消息,最后调用到IPCThreadState::executeCommand()来继续向下执行,如下:

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    ......
      case BR_TRANSACTION:
        {  
            if (tr.target.ptr) {
                if (reinterpret_cast<RefBase::weakref_type*>(
                        tr.target.ptr)->attemptIncStrong(this)) {
                    error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
                            &reply, tr.flags);
                    reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
                } 
             ......

然后调用到Binder驱动在Native的封装的BBinder::transact,如下,具体代码位于/frameworks/native/libs/binder/Binder.cpp

status_t BBinder::transact(
    uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
     ......
            err = onTransact(code, data, reply, flags);
    ......    
}

 其中这里的BBinder有两个子类:一个是JavaBBinder,就是Binder驱动在非kernel层使用Java实现的,如我们在应用层定义的Service;另外一个是BnInterface,即Binder驱动在非kernel层用C来实现的,如MediaPlayService。所以这里就是调用到JavaBBinder::onTransact,具体的代码位于/frameworks/base/core/jni/android_util_Binder.cpp的JavaBBinder。

所以之前在 Android 跨进程通信-(八)AIDL中的代理模式之源码分析 中的4.Binder.allowBlocking()提到的Server进程中的Binder驱动在Native对应的类为BBinder,完善之前的示意图如下:

2.Framework层的Binder的逻辑

在JavaBBinder::onTransact的相关代码如下:

    virtual status_t onTransact(
        uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
    {
           //这里通过jni调用到在Framework层的相关java方法 
            //gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
        jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
            code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);

这里通过虚拟机的CallBooleanMethod反向调用了一个用gBinderOffsets.mExecTransact指向Java的方法,其中对应的就是Binder中的execTransact(),这样有Native层就调用到了Framework层的Binder,如下:

    // Entry point from android_util_Binder.cpp's onTransact
    private boolean execTransact(int code, long dataObj, long replyObj,
            int flags) {
    .......
     res = onTransact(code, data, reply, flags);      
    .......

在前面我们定义了Stub来继承Binder,那么现在就会调用到AIDL文件编译生成java文件的Stub里面的onTransact()。

3.aidl编译的java文件的逻辑

在Stub的onTransact(),如下:

  @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
      java.lang.String descriptor = DESCRIPTOR;
     .......
        case TRANSACTION_setBaiduMessageTitle:
        {
          data.enforceInterface(descriptor);
          java.lang.String _arg0;
          _arg0 = data.readString();
          this.setBaiduMessageTitle(_arg0);
          reply.writeNoException();
          return true;
        }

 很显然就是调用在子类里面实现的setBaiduMessageTitle()的具体逻辑。

这样经过上面的一系列的调用,就完成了Client和Server进程的RPC。

四 总结

一个从Client到Server的RPC流程如下:

  • 1.通过bindService()让Zygote进程fork出Server进程,完成Server进程的初始化功能加载。同时建立Client和Server的关系(遗留问题:这个关系是一种什么关系呢?系统Service在通过ServiceManager注册和管理,在Binder驱动会有一个结构体来知道Client和对应的Server,那现在应用层的Service呢?
  • 2.Client进程持有的是BinderProxy,当要进行RPC的时候,会通过BinderProxy的transact()通过jni(android_utils_Binder)调用到Native层的BpBinder::transact,在BpBinder中会最终调用到IPCThreadState::transact()完成通过ioctl()信息从Client发送到Binder驱动;
    • IPCThreadState是Native层负责与Binder驱动进行交互的操作类,完成IPC的数据读写;而ProcessState为每个进程打开Binder设备文件,建立内存映射;
  • 3.Binder驱动接收到信息之后,就会找到对应的Server进程,然后从kernel通知到IPCThreadState,在IPCThreadState维护着一个死循环,该循环就会接收到消息,然后调用的BBinder::transact();
    • Server进程访问Binder驱动的封装有两种:一种是Binder驱动在非kernel层是用C实现的,那么就对应BnInterface;另外一种是在非kernel层用Java实现的,那么对应的就是JavaBBinder,两个都为BBinder的子类;
  • 4.BBinder::transact()调用到子类JavaBBinder的onTransact(),通过jni(JavaBBinder已经为jni调用)调用到Binder的onTransact(),从而完成整个流程。

简答将里面的方法调用如图所示:

 

以上是关于Android 跨进程通信-从源码分析AIDL跨进程通信实现的主要内容,如果未能解决你的问题,请参考以下文章

Android跨进程通信——AIDL原理解析

Android跨进程通信Binder机制与AIDL实例

Android跨进程通信Binder机制与AIDL实例

Android AIDL 跨进程通信超详版

Android跨进程通信AIDL服务

Android 跨进程通信Aidl的使用及注意事项