Android 四大组件 - bindService 的通信过程

相关文章链接:

1. Android Framework - 学习启动篇

2. Android Binder 驱动 - Media 服务的添加过程

3. Android Binder 驱动 - 启动 ServiceManager 进程

4. Android Binder 驱动 - 内核驱动层源码分析

5. Android Binder 驱动 - 从驱动层来分析服务的添加过程

6. Android Binder 驱动 - 从 Java 层来跟踪服务的查找过程

7. Android 四大组件 - 进程的 fork 创建过程

相关源码文件:


/frameworks/base/core/java/android/app/ContextImpl.java

/frameworks/base/core/java/android/app/ActivityManagerNative.java

/frameworks/base/core/jni/android_util_Binder.cpp

/frameworks/base/core/jni/android_os_Parcel.cpp

/frameworks/native/libs/binder/Parcel.cpp

/drivers/staging/android/binder.c

/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

/frameworks/base/core/java/android/app/ActivityThread.java

1. 代码示例

本文主要从 bindService 的跨进程通信与数据交互来入手分析,关于其具体的创建和绑定流程,大家可以看看之前的内容。aidl 一直都是一个晦涩难懂的内容,笔者在几年前也曾反复尝试去了解其原理,其过程至今想想仍然是心有余悸,因此建议大家如果是刚入手 Android ,只需要了解开发套路就可以了。但还是倡导大家,等有了一定技术沉淀之后,要尝试着去理解其内部实现原理。我们先来看一个简单的示例代码


// 客户端

public class MainActivity extends AppCompatActivity {

    // 客户端一定要获取aidl的实例

    private UserAidl mUserAidl;

    private ServiceConnection mServiceConn = new ServiceConnection() {

        @Override

        public void onServiceConnected(ComponentName name, IBinder service) {

            // 链接好了  service就是服务端给我们的 IBinder

            mUserAidl = UserAidl.Stub.asInterface(service);

        }

        @Override

        public void onServiceDisconnected(ComponentName name) {

            // 断开链接

        }

    };

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        Intent intent = new Intent(this,MessageService.class);

        bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE);

    }

    public void testClick(View view) {

        try {

            Log.e("TAG","userName = "+mUserAidl.getUserName());

        } catch (RemoteException e) {

            e.printStackTrace();

        }

    }

}

// 服务端

public class MessageService extends Service {

    @Override

    public IBinder onBind(Intent intent) {

        // 绑定

        return mBinder;

    }



    private final UserAidl.Stub mBinder = new UserAidl.Stub()

    {

        @Override

        public String getUserName() throws RemoteException {

            return "Darren@163.com";

        }

        @Override

        public String getUserPassword() throws RemoteException {

            return "19940223";

        }

    };

}

2. Binder 对象初始化

服务端返回的是 Aidl.Stub 服务对象,这个对象是我们自定义 aidl 后,编译器帮我们自动生成的一个对象,该对象继承自 Binder.java 对象,子类在构建对象时默认会执行父类的构造函数,也就是说会执行 Binder.java 的构造函数:


    public Binder() {

        // 调用初始化方法

        init();

    }

    // 调用 native 方法

    private native final void init();

    static void android_os_Binder_init(JNIEnv* env, jobject obj)

    {

      JavaBBinderHolder* jbh = new JavaBBinderHolder();

      ...

      env->SetLongField(obj, gBinderOffsets.mObject, (jlong)jbh);

    }

由上面的源码可以看出,Stub 对象在构建的时候,会对应绑定 native 层的一个 JavaBBinderHolder 对象,至于为什么要创建 native 层对象,是因为跨进程通信的核心是要基于 binder 驱动。

3. Stub 对象传递


    @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");

        }

        validateServiceIntent(service);

        try {

            IBinder token = getActivityToken();

            if (token == null && (flags&BIND_AUTO_CREATE) == 0 && mPackageInfo != null

                    && mPackageInfo.getApplicationInfo().targetSdkVersion

                    < android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH) {

                flags |= BIND_WAIVE_PRIORITY;

            }

            service.prepareToLeaveProcess();

            // 像 AMS 发起 bind 请求

            int res = ActivityManagerNative.getDefault().bindService(

                mMainThread.getApplicationThread(), getActivityToken(), service,

                service.resolveTypeIfNeeded(getContentResolver()),

                sd, flags, getOpPackageName(), user.getIdentifier());

            if (res < 0) {

                throw new SecurityException(

                        "Not allowed to bind to service " + service);

            }

            return res != 0;

        } catch (RemoteException e) {

            throw new RuntimeException("Failure from system", e);

        }

    }

由于 ServiceConnection 是一个普通对象,并不能用来做跨进程传递,所以在 bindService 的源码底层,系统帮我包装了一个可以跨进程传递的 IServiceConnection 对象,该对象是一个 Stub 对象。


    public int bindService(IApplicationThread caller, IBinder token,

            Intent service, String resolvedType, IServiceConnection connection,

            int flags,  String callingPackage, 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);

        // asBinder 返回的是 connection 自身

        data.writeStrongBinder(connection.asBinder());

        data.writeInt(flags);

        data.writeString(callingPackage);

        data.writeInt(userId);

        mRemote.transact(BIND_SERVICE_TRANSACTION, data, reply, 0);

        reply.readException();

        int res = reply.readInt();

        data.recycle();

        reply.recycle();

        return res;

    }

    static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)

    {

      Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);

      if (parcel != NULL) {

          const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));

          if (err != NO_ERROR) {

              signalExceptionForError(env, clazz, err);

          }

      }

    }

    sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)

    {

      if (obj == NULL) return NULL;

      // 返回的其实是一个 JavaBBinder

      if (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {

          JavaBBinderHolder* jbh = (JavaBBinderHolder*) env->GetLongField(obj, gBinderOffsets.mObject);

          return jbh != NULL ? jbh->get(env, obj) : NULL;

      }

      ...

      return NULL;

    }

    // 这个之前的文章有讲就不再贴代码了

    status_t Parcel::writeStrongBinder(const sp<IBinder>& val)

    {

      return flatten_binder(ProcessState::self(), val, this);

    }

上面会调用 writeStrongBinder 方法将 IServiceConnection 对象写入到 Parcel 内存中,其实也就是将 native 层的 JavaBBinder 对象写入到 native 层的 Parcel 中,关于 flatten_binder 方法的实现原理大家可以参考之前的文章。

4. 客户端驱动层处理过程

我们向 ServiceManager 查询到的 AMS 对象,其实是一个 BindProxy.java 对象,对应 native 层的 BpBinder 对象,也就是说 mRemote.transact 最后会调用 BpBinder.cpp 的 transact 方法。


static void binder_transaction(struct binder_proc *proc,

      struct binder_thread *thread,

      struct binder_transaction_data *tr, int reply)

{

if (reply) {

...

} else {

if (tr->target.handle) {

struct binder_ref *ref;

            // 找到 AMS 目标进程的 binder_node 和 binder_proc

ref = binder_get_ref(proc, tr->target.handle);

target_node = ref->node;

} else {

...

}

                ...

}

...

off_end = (void *)offp + tr->offsets_size;

for (; offp < off_end; offp++) {

struct flat_binder_object *fp;

        ...

fp = (struct flat_binder_object *)(t->buffer->data + *offp);

switch (fp->type) {

case BINDER_TYPE_BINDER:

case BINDER_TYPE_WEAK_BINDER: {

struct binder_ref *ref;

            // 先通过 fp->binder 从自己进程找 binder_node

struct binder_node *node = binder_get_node(proc, fp->binder);

            // 刚开始第一次是找不到的

if (node == NULL) {

                // 把他添加到当前进程

node = binder_new_node(proc, fp->binder, fp->cookie);

...

}

// 添加到目标进程

ref = binder_get_ref_for_node(target_proc, node);

if (ref == NULL) {

return_error = BR_FAILED_REPLY;

goto err_binder_get_ref_for_node_failed;

}

            // 替换 type 和 handle 值

if (fp->type == BINDER_TYPE_BINDER)

fp->type = BINDER_TYPE_HANDLE;

else

fp->type = BINDER_TYPE_WEAK_HANDLE;

fp->handle = ref->desc;

} break;

...

}

}

t->work.type = BINDER_WORK_TRANSACTION;

list_add_tail(&t->work.entry, target_list);

tcomplete->type = BINDER_WORK_TRANSACTION_COMPLETE;

list_add_tail(&tcomplete->entry, &thread->todo);

        // 唤醒目标进程的等待队列

if (target_wait)

wake_up_interruptible(target_wait);

return;

}

5. AMS 服务端处理 bind 过程

AMS 接收到 bindService 请求后会去读取 IServiceConnection 对象,注意这里读取到的 IServiceConnection 其实已经是本地的一个 BpBinder 了,只不过我们可以通过 BpBinder 对象回调到客户端的 onServiceConnected 绑定方法。


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();

  String callingPackage = data.readString();

  int userId = data.readInt();

  IServiceConnection conn = IServiceConnection.Stub.asInterface(b);

  int res = bindService(app, token, service, resolvedType, conn, fl, callingPackage, userId);

  reply.writeNoException();

  reply.writeInt(res);

  return true;

}

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;

}

sp<IBinder> Parcel::readStrongBinder() const

{

    sp<IBinder> val;

    unflatten_binder(ProcessState::self(), *this, &val);

    return val;

}

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;

}

6. 进程循环等待处理请求

服务端处理客户端的请求,肯定会有一个等待执行处理的过程,但整个交互逻辑流程分析下来,并没有发现服务端有等待的过程。那服务端到底是怎样进入等待的呢?这个得追溯到 Zygote 创建进程的流程去。


    public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)

            throws ZygoteInit.MethodAndArgsCaller {

        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");

        redirectLogStreams();

        commonInit();

        nativeZygoteInit();

        applicationInit(targetSdkVersion, argv, classLoader);

    }

    private static final native void nativeZygoteInit();

    static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)

    {

      gCurRuntime->onZygoteInit();

    }

    virtual void onZygoteInit()

    {

        // 初始化 binder 驱动

        sp<ProcessState> proc = ProcessState::self();

        ALOGV("App process: starting thread pool.\n");

        // 启动一个线程进入循环处理客户端 binder 请求

        proc->startThreadPool();

    }



    void ProcessState::spawnPooledThread(bool isMain)

    {

      if (mThreadPoolStarted) {

          String8 name = makeBinderThreadName();

          ALOGV("Spawning new pooled thread, name=%s\n", name.string());

          sp<Thread> t = new PoolThread(isMain);

          t->run(name.string());

      }

    }

    class PoolThread : public Thread

    {

    public:

        PoolThread(bool isMain)

            : mIsMain(isMain)

        {

        }



    protected:

        virtual bool threadLoop()

        {

            IPCThreadState::self()->joinThreadPool(mIsMain);

            return false;

        }



        const bool mIsMain;

    };

    void IPCThreadState::joinThreadPool(bool isMain)

    {

      ...

      status_t result;

      do {

          ...

          // 进入 binder 驱动的 wait 队列上进行等待

          result = getAndExecuteCommand();

          ...

      } while (result != -ECONNREFUSED && result != -EBADF);

      ...

    }

视频地址:https://pan.baidu.com/s/1qd2UafSGELpKwBsT30IuHw

视频密码:0tgv

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342