Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

从AILD与bindService谈Binder进程间通信原理(上)

$
0
0

从AILD与bindService谈Binder进程间通信原理(上)


前言

Android进程间通信可以分为三种方式:Binder机制,文件读写,Socket机制。这篇文章主要就是来谈谈Binder机制实现进程间通信的原理,主要分析AIDL进程间通信和bindService方法涉及的进程间数据传输相关逻辑。

AIDL实现进程间通信:

通过AIDL具体如何实现进程间通信,我推荐阅读以下文章:
Android:学习AIDL,这一篇文章就够了(上)
Android:学习AIDL,这一篇文章就够了(下)

这两篇文章把AIDL进程间通信的具体实现和代码原理进行了详细的讲解。
但是,我看到这里产生了2个疑惑:

1,服务端进程并没有开启一个线程去处理远程服务请求,如何服务?

2,与系统服务的Binder通信方式比较,并没有发现向ServiceManager注册服务和请求服务的过程,如何注册和获取服务?且AIDL中远程数据是如何跨进程传输的

1-答:Android进程本身就支持Binder进程间通信机制。因为安卓进程通过Zygote创建的时候,已经在AppRuntime的onZygoteInit()方法中调用了startThreadPool()方法。

2-答:Binder通信并非必须通过ServiceManager.addService(String name, IBinder service)来向ServiceManager进程注册服务,还可以通过Parcel中的writeStrongBinder方法把JavaBBinder类型Binder实体添加到ServiceManager中;而且并非必须使用ServiceManager.getService(String name)获得BpBinder对象,还可以通过Parcel中的readStrongBinder获取一个BpBinder对象用于请求远程服务。

就以上二个问题详细分析AIDL实现的具体逻辑:

1 Android进程本身就支持Binder进程间通信机制。安卓进程通过Zygote创建的时候,已经在AppRuntime的onZygoteInit()方法中调用了startThreadPool()方法创建了一个用于Binder通信的线程,因此,我们自己创建的远程服务不需要再额外创建一个新的线程去处理远程请求。

具体的android进程启动,推荐查看下面的文章:
Android应用程序进程启动过程的源代码分析

2 结合AIDL实际来说明这个问题

首先,我们看看AIDL实现的关键代码:
1,Binder核心代码:

public static abstract class Stub extends android.os.Binder implements MyAIDL
{

public static MyAIDL asInterface(android.os.IBinder obj)
{
    if ((obj==null)) {
        return null;    
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof MyAIDL))) {
        return ((MyAIDL)iin);
    }
    return new MyAIDL.Stub.Proxy(obj);
}

@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_getValue:
        {
            data.enforceInterface(DESCRIPTOR);
            java.lang.String _result = this.getValue();
            reply.writeNoException();
            reply.writeString(_result);
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

2,BinderProxy核心代码:

private static class Proxy implements MyAIDL
{
    private android.os.IBinder mRemote;
    Proxy(android.os.IBinder remote)
    {
        mRemote = remote;
    }
    @Override public android.os.IBinder asBinder()
    {
    return mRemote;
    }

    @Override public java.lang.String getValue() 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.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_getValue, _data, _reply, 0);
            _reply.readException();
            _result = _reply.readString();
        }
        finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}

3,客户端关键代码:

public class MainActivity extends Activity {
    MyAIDL aidlMain;

    @Override
    protected void onCreate(Bundle savedInstanceState) {

        …………………………  
        ServiceConnection mServiceConnection=new ServiceConnection() {
            …………………………
           @Override
           public void onServiceConnected(ComponentName name, IBinder service){
                // TODO Auto-generated method stub
                aidlMain=AIDL_TESTInterface.Stub.asInterface(service);
            }
        };
        ……………………………………
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

4,服务端核心代码:

public class AIDLtestService extends Service{
    MyAIDL.Stub mainAIDL=new MyAIDL.Stub() {    
        @Override
        public String getValue() throws RemoteException {
            return "hello world~~";
        }
    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        return mainAIDL;
    }

}

这里我们主要是分析Client如何通过bindService方法获取到Server的BinderProxy对象实现进程间通信的。主流程图:

这里写图片描述

1,首先我们在客户端关键代码中可以发现,客户端通过bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)方法来启动和请求远程服务。

2,Activity实际上调用的是ContextImpl中的bindService方法

    @Override
    public boolean bindService(Intent service, ServiceConnection conn,
            int flags) {
        warnIfCallingFromSystemProcess();
        return bindServiceCommon(service, conn, flags, Process.myUserHandle());
    }

    private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags,UserHandle user) {
        IServiceConnection sd;
         ……………………………………
        if (mPackageInfo != null) {
            sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(),
            mMainThread.getHandler(), flags);
        } else {
            throw new RuntimeException("Not supported in system context");
        }
        ……………………
        int res = ActivityManagerNative.getDefault().bindService(
                mMainThread.getApplicationThread(), getActivityToken(),
                service, service.resolveTypeIfNeeded(getContentResolver()),
                sd, flags, user.getIdentifier());
        ……………………

    }

这里需要注意的是,IServiceConnection sd = mPackageInfo.getServiceDispatcher(conn,……),通过客户端自定义的ServiceConnection对象创建了一个关键的Binder,该Binder会被保存在AMS中,最后当远程服务准备好的时候,最终通过该Binder来与客户端通信。mMainThread.getApplicationThread()返回的是Client进程用于与AMS所在进程进行远程通信的Binder对象。

3,通过ActivityManagerNative.getDefault()获取ActivityManagerNative的BinderProxy对象(这里其实是通过与ServiceMangaer进程通信获取的),并且调用其bindService方法。

   public int bindService(IApplicationThread caller, IBinder token,
      Intent service, String resolvedType, IServiceConnection connection,
      int flags, int userId) throws RemoteException {
        Parcel data = Parcel.obtain();
        Parcel reply = Parcel.obtain();
        data.writeInterfaceToken(IActivityManager.descriptor);
        data.writeStrongBinder(caller != null ? caller.asBinder() : null);
        data.writeStrongBinder(token);
        service.writeToParcel(data, 0);
        data.writeString(resolvedType);
        data.writeStrongBinder(connection.asBinder());
        data.writeInt(flags);
        data.writeInt(userId);
        mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);
        reply.readException();
        int res = reply.readInt();
        data.recycle();
        reply.recycle();
        return res;
    }

4,这里调用了三次writeStrongBinder方法,即在Parcel包中写入了3个Binder对象,分别是caller.asBinder代表客户端的Binder对象(用于AMS与客户端通信),token代表什么我暂时没搞明白(^ ^),connection.asBinder()代表2中所述的,将被保存在AMS中,最终用于Client和Server远程通信。

5,writeStrongBinder会调用原生层的android_os_Parcel_writeStrongBinder方法,该方法中首先调用ibinderForJavaObject方法把Binder对象转换成一个JavaBBinder对象,然后调用parcel->writeStrongBinder,把Binder打包到Parcel中。

6,ibinderForJavaObject方法把Binder对象转换成一个JavaBBinder对象,因为传入的是一个Binder所以调用的是jbh->get(env, obj)方法创建JavaBBinder(其中还会创建JavaBBinderHolder,这个好像就是包装了一下JavaBBinder),如果传入的是一个BinderProxy对象则会调用下面半段的代码。

sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{
    if (obj == NULL) return NULL;

    if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {
        JavaBBinderHolder* jbh = (JavaBBinderHolder*)
            env->GetLongField(obj, gBinderOffsets.mObject);
        return jbh != NULL ? jbh->get(env, obj) : NULL;
    }

    if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {
        return (IBinder*)
            env->GetLongField(obj, gBinderProxyOffsets.mObject);
    }

    ALOGW("ibinderForJavaObject: %p is not a Binder object", obj);
    return NULL;
}

7,parcel->writeStrongBinder会调用方法flatten_binder,该方法把binder写入一个flat_binder_object 对象中,因为Binder是一个JavaBBinder对象,因此local不为空,该对象类型申明是obj.type = BINDER_TYPE_BINDER;

status_t flatten_binder(const sp<ProcessState>& /*proc*/,
    const sp<IBinder>& binder, Parcel* out)
{
    flat_binder_object obj;

    obj.flags = 0x7f | FLAT_BINDER_FLAG_ACCEPTS_FDS;
    if (binder != NULL) {
        IBinder *local = binder->localBinder();
        if (!local) {
            BpBinder *proxy = binder->remoteBinder();
            if (proxy == NULL) {
                ALOGE("null proxy");
            }
            const int32_t handle = proxy ? proxy->handle() : 0;
            obj.type = BINDER_TYPE_HANDLE;
            obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
            obj.handle = handle;
            obj.cookie = 0;
        } else {
            obj.type = BINDER_TYPE_BINDER;
            obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
            obj.cookie = reinterpret_cast<uintptr_t>(local);
        }
    } else {
        obj.type = BINDER_TYPE_BINDER;
        obj.binder = 0;
        obj.cookie = 0;
    }

    return finish_flatten_binder(binder, obj, out);
}

finish_flatten_binder方法会调用out->writeObject(flat, false);把该对象写入Parcel。writeStrongBinder方法就结束了。writeStrongBinder方法主要就是把Binder写入了Parcel中去,为了远程服务器从该Parcel中获取BinderProxy对象,从而可以与对应的Binder客户端远程通信

8,mRemote是ServiceManager.getService(“activity”)的返回值,是一个BinderProxy对象。因此,mRemote.transact将会进入原生层的方法调用。

9,将调用BinderProxy的transactNative方法,进入原生层的android_os_BinderProxy_transact方法,该方法把java层的Parcel转换成Native层的Pacel对象,把java层的BinderProxy对象转换成Native层的BpBinder对象。

static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
       jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
    //只截取了关键代码
    Parcel* data = parcelForJavaObject(env, dataObj);
    Parcel* reply = parcelForJavaObject(env, replyObj);
    IBinder* target = (IBinder*)
        env->GetLongField(obj, gBinderProxyOffsets.mObject);
    status_t err = target->transact(code, *data, reply, flags);
}

10-11,然后调用BpBinder的transact方法,将会调用IPCThreadState的transact方法。

12,在IPCThreadState的transact方法中通过writeTransactionData方法把传入的Parcel数据先转换成专用于Binder数据传输的binder_transaction_data实例,然后将其与指令BC_TRANSACTION写入到IPCThreadState专门用于与BinderDriver交互的Parcel对象mOut中。

13-15,调用waitForResponse方法,该方法首先调用talkWithDriver方法,该方法此时是把数据写入BinderDriver中,而当服务端读取时,该方法则是从BinderDriver获取数据。接着,方法将调用ioctl方法与BinderDriver驱动交互,交互的指令是BINDER_WRITE_READ。

16,在kernel中的BinderDriver,通过binder_ioctl获取ioctl传入的数据,并根据指令进一步响应。BinderDriver是一块共享内存,可以通过文件/dev/binder访问,所有的进程都可以读取这块内存(不知道这么理解是不是有点问题。。。)。

17,在kernel中主要是在binder_transaction方法中做了一些事情,在第7步中,我们的Binder对象类型是BINDER_TYPE_BINDER,如果待通信的进程是自身进程,那么该值不变,如果待通信的进程是远程进程,则该值变为BINDER_TYPE_HANDLE。并且在该方法中会为Binder对象的handle赋值,进程通信就是通过这个handle来区分不同的BpBinder对象(XXX.asInterface(new BpBinder(handle)))。

通过16-17步,Client的进程通信请求已经写入BinderDriver中了,客户端操作暂时告以段落,此时,服务端进程开始准备获取该请求并响应。


接下来进入到系统服务system_Server进程中的ActivityManagerService服务的过程,因此

18-19,服务端在 IPCThreadState::joinThreadPool方法中,处于无限循环,不断的调用getAndExecuteCommand方法来获取BD中的数据,并执行命令。

status_t IPCThreadState::getAndExecuteCommand()
{
    status_t result;
    int32_t cmd;

    result = talkWithDriver();
    if (result >= NO_ERROR) {
        size_t IN = mIn.dataAvail();
        if (IN < sizeof(int32_t)) return result;
        cmd = mIn.readInt32();
        IF_LOG_COMMANDS() {
            alog << "Processing top-level Command: "
                 << getReturnString(cmd) << endl;
        }

        result = executeCommand(cmd);
        set_sched_policy(mMyThreadId, SP_FOREGROUND);
    }

    return result;
}

同之前一样,也是通过talkWithDriver方法从BD获取数据,然后执行executeCommand方法。

20,在executeCommand方法中,把之前第11步中创建的binder_transaction_data 对象重新写入IPCThreadState专用的Parcel对象mIn中,然后调用Parcel的ipcSetDataReference方法重新构造mRemote.transact中传递的Parcel对象。最后创建AMS的Binder对象,并调用其transact方法。

status_t IPCThreadState::executeCommand(int32_t cmd)
{
    BBinder* obj;
    switch (cmd) {
     ……………………………………//仅保留关键代码
    case BR_TRANSACTION:
        {
            binder_transaction_data tr;
            result = mIn.read(&tr, sizeof(tr));
            Parcel buffer;
            buffer.ipcSetDataReference(
                reinterpret_cast<const uint8_t*>(tr.data.ptr.buffer),
                tr.data_size,
                reinterpret_cast<const binder_size_t*>(tr.data.ptr.offsets),
                tr.offsets_size/sizeof(binder_size_t), freeBuffer, this);



            if (tr.target.ptr) {
                sp<BBinder> b((BBinder*)tr.cookie);
                error = b->transact(tr.code, buffer, &reply, tr.flags);

            } 


    }

}

21,BBinder->transact方法会最终调用ActivityManagerNative的onTransact方法。

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
            throws RemoteException {
        switch (code) {
        //仅关键代码,下面不在赘述,代码基本上都只保留了关键的。。
        case BIND_SERVICE_TRANSACTION: {
            data.enforceInterface(IActivityManager.descriptor);
            IBinder b = data.readStrongBinder();
            IApplicationThread app = ApplicationThreadNative.asInterface(b);
            IBinder token = data.readStrongBinder();
            Intent service = Intent.CREATOR.createFromParcel(data);
            String resolvedType = data.readString();
            b = data.readStrongBinder();
            int fl = data.readInt();
            int userId = data.readInt();
            IServiceConnection conn = IServiceConnection.Stub.asInterface(b);
            int res = bindService(app, token, service, resolvedType, conn, fl, userId);
            reply.writeNoException();
            reply.writeInt(res);
            return true;
        }
}        

在该方法中,解包获取了之前Client中打包进入Parcel中的数据,包括Intent,三个Binder对象和其他数据。但是这里的Binder对象已经变成了BinderProxy对象。对象的转变发生在readStrongBinder函数中。然后分别将的BinderProxy对象封装成自己的远程服务代理,

22,readStrongBinder会调用原生层的android_os_Parcel_readStrongBinder方法,该方法首先把java层的Parcel转换成Native层的Parcel,然后通过该parcel去读取哭护短传来的Binder

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{
    Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);
    if (parcel != NULL) {
        return javaObjectForIBinder(env, parcel->readStrongBinder());
    }
    return NULL;
}

24-25,Parcel::readStrongBinder()方法调用unflatten_binder方法

status_t unflatten_binder(const sp<ProcessState>& proc,
    const Parcel& in, sp<IBinder>* out)
{
    const flat_binder_object* flat = in.readObject(false);

    if (flat) {
        switch (flat->type) {
            case BINDER_TYPE_BINDER:
                *out = reinterpret_cast<IBinder*>(flat->cookie);
                return finish_unflatten_binder(NULL, *flat, in);
            case BINDER_TYPE_HANDLE:
                *out = proc->getStrongProxyForHandle(flat->handle);
                return finish_unflatten_binder(
                    static_cast<BpBinder*>(out->get()), *flat, in);
        }
    }
    return BAD_TYPE;
}

由于我们是跨进程调用,在kernel的binder_transaction方法中,binder的类型被修改成了BINDER_TYPE_HANDLE,并且binder被赋予了唯一的handle数值,因此,通过proc->getStrongProxyForHandle(flat->handle)方法,获得了这个BpBinder对象。

26,通过javaObjectForIBinder把Native层的BpBinder对象转换成java层的BinderProxy对象。

jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{
    if (val == NULL) return NULL;

    if (val->checkSubclass(&gBinderOffsets)) {
        // One of our own!
        jobject object = static_cast<JavaBBinder*>(val.get())->object();
        LOGDEATH("objectForBinder %p: it's our own %p!\n", val.get(), object);
        return object;
    }

    // For the rest of the function we will hold this lock, to serialize
    // looking/creation of Java proxies for native Binder proxies.
    AutoMutex _l(mProxyLock);

    // Someone else's...  do we know about it?
    jobject object = (jobject)val->findObject(&gBinderProxyOffsets);
    if (object != NULL) {
        jobject res = jniGetReferent(env, object);
        if (res != NULL) {
            ALOGV("objectForBinder %p: found existing %p!\n", val.get(), res);
            return res;
        }
        LOGDEATH("Proxy object %p of IBinder %p no longer in working set!!!", object, val.get());
        android_atomic_dec(&gNumProxyRefs);
        val->detachObject(&gBinderProxyOffsets);
        env->DeleteGlobalRef(object);
    }

    object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);
    if (object != NULL) {
        LOGDEATH("objectForBinder %p: created new proxy %p !\n", val.get(), object);
        // The proxy holds a reference to the native object.
        env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());
        val->incStrong((void*)javaObjectForIBinder);

        // The native object needs to hold a weak reference back to the
        // proxy, so we can retrieve the same proxy if it is still active.
        jobject refObject = env->NewGlobalRef(
                env->GetObjectField(object, gBinderProxyOffsets.mSelf));
        val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env), proxy_cleanup);

        // Also remember the death recipients registered on this proxy
        sp<DeathRecipientList> drl = new DeathRecipientList;
        drl->incStrong((void*)javaObjectForIBinder);
        env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));

        // Note that a new object reference has been created.
        android_atomic_inc(&gNumProxyRefs);
        incRefsCreated(env);
    }

    return object;
}

在该方法中首先通过val->findObject查找BpBinder对象是否已经创建并保存过了,这里的val是一个BpBinder对象,因此findObject是定义在BpBinder.cpp文件中,在BpBinder内通过一个ObjectManager对象来管理存储BpBinder对象的。

jobject object = (jobject)val->findObject(&gBinderProxyOffsets);

如果找到则返回该BpBinder对象对应的BinderProxy对象。如果没有则通过BinderProxy的类型和构造函数,创建一个BinderProxy对象

object = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);

gBinderProxyOffsets保存的是Java层的BinderProxy的类信息,就像是Java层的BindProxy类在Native层类的映射,gBinderProxyOffsets是在Binder初始化时的int_register_android_os_BinderProxy方法创建的,通过gBinderProxyOffsets对象把Native层的BpBinder转换成BinderProxy对象,BpBinder的实例保存在BinderProxy的mObject成员中。

 env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());

最后通过BpBinder的attachObject方法,把这个创建的BinderProxy对象加入到BpBinder的ObjectManager对象中进行管理,下次就可以直接通过findObject来找到该对象了。

        val->attachObject(&gBinderProxyOffsets, refObject,
                jnienv_to_javavm(env), proxy_cleanup);

通过上述方法AMS所在进程就把Client发送过来的Binder对象对应的BpBinder对象转换成BinderProxy对象传递到了框架层。

回到第21步中,在框架层的onTransact中取回了transact中的三个Binder对象对应的BinderProxy对象,并封装成各自的通信代理对象XXXXProxy,例如app的类型是ApplicationThreadProxy。

IApplicationThread app = ApplicationThreadNative.asInterface(b);
IBinder token = data.readStrongBinder();
IServiceConnection conn = IServiceConnection.Stub.asInterface(b);

这样子,AMS就可以通过app对象与Client进程远程通信;conn 对象将被保存在AMS中也是为了AMS与Client远程通信,conn 对象最后会用于传输Server的BpBinder对象回Client进程中。

27,开始了真正的bindService操作,具体实现是在ActivityManagerService类中。


通过1-27步骤,我们就可以解决一般的问题了,为什么不需要通过ServiceManager.addService注册服务,不需要通过ServiceManager.getService请求远程服务就可以进行进程间通信了。因为,ServiceManager.getService返回的是远程服务的BinderProxy,所以只要能获得远程服务的BinderProxy就可以与该远程服务通信了。就像Client进程与AMS所在进程通信一样,Client进程把自身用于进程间通信的Binder对象和远程服务请求,同时通过AMS的远程代理发送给了AMS服务,AMS服务就拥有了Client进程的远程服务代理对象BinderProxy。通过该对象,AMS就可以向Client发出远程请求。

看到这里,大家会发现,AMS是通过ServiceManager.addService注册了服务的,这样子Client才能向他通信啊,向他发送自己的BinderProxy对象。那么自己定义的Server并没有通过ServiceManager.addService注册服务。那么还是无法远程通信啊。

其实不然,仔细思考一下,只要他们能够互相知道并拥有对方的BinderProxy,那么他们就能够进行远程通信。大家可以猜测一下Client与Server是如何完成进程间通信的,具体分析,留在下一篇文章中。下一篇文章将分析剩下的:………………………………

本文上半部分总结


仅对问题具体逻辑分析部分总结

1-2,Client进程发送bindService请求前的数据准备;
3-7,调用AMS的远程代理准备发送请求前,将Binder等数据打包到Parcel中;
8-17,程代理向共享内存的BD写入请求,并对当前Client进程打包到Parcel中的Binder做相关处理;
18-21,远程进程从BD获取远程请求和数据
22-26, 解包数据,并获取Client进程传递过来的所有BinderProxy代理,将用于与Client进程通信。
27, 正式调用远程请求的方法bindService

其中1-17是在Client进程中完成,18-27是在AMS所在的进程中完成。

本文上半部分主要讲解了Client向AMS发送请求的具体过程,中间涉及到一个概念就是Client把自身的Binder对象通过Parcel包发送给远程进程AMS的onTransact方法的时候,Binder对象变成了BinderProxy对象。Java层的Binder对象与C层的BBinder对象对应,Java层的BinderProxy对象与C层的BpBinder对应。

另一个概念就是进程间通信的关键就在于,A进程拥有B进程的BinderProxy(BpBinder),A进程通过B进程的远程代理就可以向B进程发送请求和信息。

作者:zero9988 发表于2017/6/23 14:09:03 原文链接
阅读:615 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>