Binder 浅出深入 -- 你真的会用 binder?

做过 Android 跨进程通信的同学应该都知道 Binder 是个什么东西。有了他我们可以很方便的调用另外一个进程中的方法。

如果看过 Android Framework 层源码的话,更会发现说,大部分都是在和 binder 打交道。

所以 Binder 这套跨进程通信机制(IPC)对 Android 开发通信来说是非常重要的。

但是想要深入的了解总是感觉有点力不从心, 下面来一起浅出深入的了解 Binder。

如何使用 AIDL

都知道在 Android 开发中如果跨进程件通信需要使用 AIDL的方式.
主要有以下几个步骤

  1. 定义 aidl 接口文件
  2. 等待 IDE 生成相关的 java 文件
  3. 在 Service 中的 onBind()方法中 返回对应的 Binder 对象
  4. 在 Activity 中调用 bindService()方法。 并在回调中获取 Binder 对象
  5. 吧 binder 对象转换成我们定义的接口类型, 在调用我们定义接口方法

关于更加详细的使用步骤请看我之前写的一篇博客.AIDL使用学习

你真的会用 binder?

跨进程通信,使用方式都知道. 但是大多的时候是知其然, 不知其所以然. 我们先不讨论深入的细节原理.
我先来思考一下几个问题.

  1. 当 A 进程在主线程中调用 B 进程中的某个方法时, B 进程中执行该方法的是哪个线程?

  2. 当 A 进程通过 bindService 分别连上 B 进程 和 C 进程. 那么 B C之间不通过 bindService 是否能够通信?

  3. 当 A 进程通过 bindService 连上 B 进程后, 如何同 B 进程调用 A 进程中的方法?

先来回答第一个问题:
这个问题只要自己尝试一下打个 log 就能够知道, B 进程中执行该方法的不是主线程, 而是一个和 binder 相关的线程, 打出来的 log 如下:

ServiceB: basicTypes: B Thread[Binder_1,5,main]

很明显这个线程,不是我们创建的, 是 Binder 这套机制给我创建.

如果属性 Android 源码的话会发现, 基本上每次 AMS 通过 Binder 调用 ActivtyThread 中的方法, 都要在重新通过 Hander 机制在处理一下. 这是因为跨进程调用的那个方法不是主线程. 如果要在该方法中执行主线程的一些方法, 需要通过 Hnader 机制才行.

那为什么会出现这个一个 binder线程呢? 可以好好想想.

再来回答第二个问题:
我们一般跨进程通信第一时间想到的肯定是说 AIDL bindService 来执行. 那 B和 C 之前不 Binder Service怎么跨进程通信呢?

我们下来看看 bindService 中我们拿到的是什么.

bindService(intent, new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mBBinder = service;
        IAtoBAidlInterface stub = IAtoBAidlInterface.Stub.asInterface(mBBinder);
        try {
            stub.basicTypes(1, 2l, false, 1.0f, 2.0, "test");
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.d(TAG, "B onServiceDisconnected() called with: name = [" + name + "]");

    }
}, Context.BIND_AUTO_CREATE);

从面的代码我们可以看到, 我们在 onServiceConnected可以获取到参数 service, 他是一个IBinder对象, 然后我们在通过IAtoBAidlInterface.Stub.asInterface(mBBinder)方法转换成我们需要的接口类型, 然后就可以顺利的执行方法了, 而且执行的 B 进程中的方法.

这里的重点是, 我们拿到一个 IBinder 对象, 然后把它转成了我们要的接口类型. 然后就可以直接调用另外一个进程的方法(感觉上是这样的)

在看看问题和条件.
条件: 当 A 进程通过 bindService 分别联上 B 进程 和 C 进程
问题: B 和 C 之前不 Binder Service怎么跨进程通信呢

从条件中我们可以得到的信息是: A 进程中持有两个我们上面说的IBinder对象, 一个是 B 进程的, 一个是 C 进程的. 这里我们名这两个对象为binderBbinderC

那我们大胆的猜想一下. 把binderC 传递给 B 进程, 那 B 进程是不是直接可以根据这个binderC对象直接调用 C 进程中的方法呢? 可以尝试一下.

我们在 A 调用 B 进程的 AIDL 文件上中加入一个 addBinder() 方法,

interface IMyAidlInterface {
    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
            double aDouble, String aString);

    void addBinder(android.os.IBinder binder);
}

然后在 B 进程中实现该方法:

IAtoBAidlInterface.Stub mBinder = new IAtoBAidlInterface.Stub() {
    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
        Log.e(TAG, "basicTypes: B " + Thread.currentThread());
        Log.d(TAG, "basicTypes() called with: anInt = [" + anInt + "], aLong = [" + aLong + "], aBoolean = [" + aBoolean + "], aFloat = [" + aFloat + "], aDouble = [" + aDouble + "], aString = [" + aString + "]");
    }

    @Override
    public void addBinder(IBinder binder) throws RemoteException {
        Log.e(TAG, "addBinder() called with: binder = [" + binder + "]");
        // 把 binder 对象转换成 对应的接口类型
        IAtoCAidlInterface iAtoCAidlInterface = IAtoCAidlInterface.Stub.asInterface(binder);
        iAtoCAidlInterface.basicTypes(2, 3l, false, 1f, 2.3, "B call c");

    }
};

然后我们在在 A 进程中调用 addBinder 方法试试看. 查看 log 日志发现, 果然 B 进程成功调用了 C 进程中的方法.


原来不通过 bindService 也可以两个进程之间通信.可以想一想那我们为什么一般还是用 bindService 呢?

再来看第三个问题
通过上面的实验, 我们发现我们拿到了 B 进程对应的binderB对象, 那么理论上我们弄一个 A 进程的 binder 对象, 然后通过 binderB 传给 B 进程.

那么怎么弄一个 A进程的 binder 呢? 可以看看 Service 中是怎么弄出一个 Binder 对象来的, 他是使用我们之前定义adil 文件中自动生成的 Stub 类来 new 出一个 Binder 对象的. 我们也可以这么干.

我们先定义一个A进程中的 Binder 对象:

// 这里为了方便起见使用的是 IAtoCAidlInterface 的接口
IAtoCAidlInterface.Stub mLocalBinder = new IAtoCAidlInterface.Stub() {
    @Override
    public void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat, double aDouble, String aString) throws RemoteException {
        Log.e(TAG, "ActivityA basicTypes: C " + Thread.currentThread());
        Log.d(TAG, "ActivityA basicTypes() called with: anInt = [" + anInt + "], aLong = [" + aLong + "], aBoolean = [" + aBoolean + "], aFloat = [" + aFloat + "], aDouble = [" + aDouble + "], aString = [" + aString + "]");
    }

};

然后我们把该对象通过binderB对象传递到 B 进程中, 然后在 B 进程中调用basicTypes 方法, 查看日志发现, 果然B 进程通过我们传过去的 binder 调用 A 进程中的方法.

看到这里你会发现, 跨进程通信其实很简单, 只要我们有一个 Binder 对象, 然后传递给另一个进程. 这样相互之间就能够通信了.
其实如果看过 Android Framework 层源码的人会发现里面包含了大量的这种 Binder 传来传去的通信方式.

我们通过 bindService方式去连接 B 进程,其实也是为了 B 进程中的 binder 能够传递到 A 进程中, 不然凭空的两个进程之间也不能传递数据啊.

注意

我上面说的所有的 binder 对象从这个进程传递到另一个进程只是为了方便理解.
其实 A 进程中的 binderA 传到到 B 进程中就不是原来的 binder 对象了. 这是毋庸置疑的, 因为两个进程之间内存是分开的, 对象自然也是分开的. 但是我们依然可以用这个假的 binder 对象去调用远程进程的方法. 这就是 Binder 的高明之处.

结论

  1. 如果 A进程想要和 B 进程通信, 那么只要 A 进程拿到 B 进程的 Binder(无论真假) 对象即可
  2. 为了 A 进程能够方便的拿到 B进程的 Binder 对象, 我们需要通过 bindService 方式来获取 Binder 对象
  3. 通过跨进程调用而执行到的方法, 不是在主线程中执行的, 而是在一个和 binder 相关的线程中执行的

这篇文章只是简单讲述了 binder 的常规用法和非常规用法. 我想你应该理解 binder 的一些特性了, 希望你能通过这些特性更加的了解 Binder.

也只有了解了这些特性,才能更加方便的浅出深入的去理解 binder

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

推荐阅读更多精彩内容