安卓开发 Binder连接池

综述

    安卓IPC(进程间通信)可以利用AIDL(Android Interface definition language)来实现,通过Service返回一个IBinder来实现进程间通信。但是随着我们项目需求的增加,我们总不能每个模块都开启一个service吧?那么有没有一种方案可以让一个service同时管理我们所有的IPC业务呢?
    答案当然是肯定的,《Android开发艺术探索》给出了一个Binder连接池的概念,我们都知道利用AIDL进行进程间通信原理其实是Service端为我们创建了一个Binder,并把这个Binder的实例返回给客户端,我们就可以用这个Binder跟service层通信了。我们只需要让service返回不同的Binder就可以实现连接池的功能。

实现

我们先定义一个IBinderPool实现连接池的AIDL:

// IBinderPool.aidl
package com.liujiakuo.learnmvp;

interface IBinderPool {

    IBinder getBinder(int binderCode);

}

这个AIDL定义了一个接口方法,它通过传入的BinderCode来获取一个IBinder。
接下来我们定义两个不同的AIDL来实现不同的功能,定义一个ISum.aidl来实现求和的功能,一个IMax.aidl实现求最大值的功能。

// ISum.aidl
package com.liujiakuo.learnmvp;

interface ISum {

    int sum(int a,int b);
}
// IMax.aidl
package com.liujiakuo.learnmvp;

interface IMax {

    int max(in int[] values);
}

我们实现上面两个接口:

public class SumImpl extends ISum.Stub {

    @Override
    public int sum(int a, int b) throws RemoteException {
        return a+b;
    }
}
public class MaxImpl extends IMax.Stub {
    @Override
    public int max(int[] values) throws RemoteException {
        int max = Integer.MIN_VALUE;
        for (int v :values) {
            if(v>max)
                max=v;
        }
        return max;
    }
}

Service的写法:BinderPoolService.java

import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.util.Log;

public class BinderPoolService extends Service {
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return new BinderPoolImpl();
    }

    class BinderPoolImpl extends IBinderPool.Stub {
        @Override
        public IBinder getBinder(int binderCode) {
            IBinder binder = null;
            switch (binderCode) {
                case BinderPoolUtils.BINDER_MAX:
                    Log.d("TAG", "getBinder: "+BinderPoolUtils.BINDER_MAX);
                    binder = new MaxImpl();
                    break;
                case BinderPoolUtils.BINDER_SUM:
                    Log.d("TAG", "getBinder: "+BinderPoolUtils.BINDER_SUM);
                    binder = new SumImpl();
                    break;
            }
            return binder;
        }
    }
}

我们来写一个工具类来为我们绑定Service并拿到我们想要的Binder。

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;

import java.util.concurrent.CountDownLatch;

public class BinderPoolUtils {
    public static final int BINDER_SUM = 1;
    public static final int BINDER_MAX = 2;

    private static BinderPoolUtils poolUtils;
    private Context context;
    private CountDownLatch countDownLatch;
    private IBinderPool mBinderPool;


    private BinderPoolUtils(Context context) {
        this.context = context;
        bindServicePool();
    }

    private ServiceConnection connection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinderPool = IBinderPool.Stub.asInterface(service);
            try {
                mBinderPool.asBinder().linkToDeath(deathRecipient, 0);//监听Binder的存货状态
            } catch (RemoteException e) {
                e.printStackTrace();
            }
            countDownLatch.countDown();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {}
    };
    private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
        @Override
        public void binderDied() {//Binder死掉之后重新绑定
            mBinderPool.asBinder().unlinkToDeath(deathRecipient, 0);//重置死亡状态
            mBinderPool = null;
            bindServicePool();//重新连接
        }
    };

    private void bindServicePool() {
        countDownLatch = new CountDownLatch(1);//同步
        Intent intent = new Intent(context, BinderPoolService.class);
        context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static BinderPoolUtils getInstance(Context context) {
        if (poolUtils == null) {
            synchronized (BinderPoolUtils.class) {
                if (poolUtils == null) {
                    poolUtils = new BinderPoolUtils(context);
                }
            }
        }
        return poolUtils;
    }

    public IBinder getBinder(int binderCode) {
        IBinder binder = null;
        try {
            binder = mBinderPool.getBinder(binderCode);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
        return binder;
    }
}

代码很简单,就是实现了一个单例模式的工具类,bindServicePool负责绑定service,countDownLatch负责阻塞线程,因为bindService()是异步的,onServiceConnected方法不会马上回调,这样我们如果在回调之前执行getBinder会有空指针问题,所以利用CountDownLatch在绑定时阻塞,在onServiceConnected回调拿到Binder之后释放。但是这里要注意的是在activity里面调用的时候要考虑线程阻塞的问题。

用法

我是在MainActivity的onCreate里面这样写的:

new Thread(new Runnable() {
            @Override
            public void run() {
                BinderPoolUtils pool = BinderPoolUtils.getInstance(MainActivity.this);
                //try {
                    //IMax maxBinder = IMax.Stub.asInterface(pool.getBinder(BinderPoolUtils.BINDER_MAX));
                    //ISum sumBinder = ISum.Stub.asInterface(pool.getBinder(BinderPoolUtils.BINDER_SUM));
                    //Log.d("TAG", "max: "+maxBinder.max(new int[]{1,2,3,-1}));
                    //Log.d("TAG", "sum: "+sumBinder.sum(1,7));
               //} catch (Exception e) {
                    //e.printStackTrace();
                //}
            }
        }).start();

为了不阻塞UI线程,我开启一个新的线程去执行。注释部分是获取相应的Binder,这部分可能要跟控件的事件绑定,当getInstance执行完之后,IBinderPool实例就拿到了,这时候可以利用handler通知主线程绑定点击事件,要不然会出现上面提到的空指针。

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

推荐阅读更多精彩内容

  • 一、IPC简介 (1)IPC是Inter-Process Communication的缩写,含义为进程间通信或者跨...
    遥遥的远方阅读 7,193评论 0 3
  • 本文介绍Service与Activity之间的通信,文章包含以下内容: 一、Service基本用法 二、通过AID...
    developerzjy阅读 10,223评论 7 27
  • 【Android Service】 Service 简介(★★★) 很多情况下,一些与用户很少需要产生交互的应用程...
    Rtia阅读 3,138评论 1 21
  • 若人生是一场旅行,那么无论繁华与落寂,都是过眼烟云,留下的是看风景的心情,有时候,让我们念念不忘的,也仅仅只是路过...
    bj浅笑安然阅读 486评论 0 1
  • 晚上在单位加班,兽医盆友急急忙忙打过电话来,说邯郸亲戚家吃涮羊肉中毒,老公加班回来发现老婆孩子头晕呕吐不止,立即拉...
    刘会芳阅读 970评论 1 1