IPC之Binder连接池

参考Android开发艺术探索

Binder连接池

什么是Binder连接池?为什么要使用它?

之前的文章中我们分析了,Binder的使用以及AIDL的使用。大致流程就是:首先创建一个Service 和一个 AIDL 接口,接着创建一个类继承自 AIDL 接口中的Stub 类并实现Stub中的抽象方法,在Service 的onBind 方法中返回这个类的对象,然后客户端就可以绑定服务端Service,建立连接之后,就可以访问远程服务端的方法了。

上述属于典型的AIDL的使用流程。那么当我们的项目越做越大,假如有10个不同的业务模块都需要使用AIDL进行通信呢?甚至100个呢?我们不可能按照标准流程创建100个Service进行通信吧,真要是这样的话,我们的APP在手机上运行时绝对是重量级的了。所以在这种大型的项目中我们寻找到一种让APP显得很轻量级的解决方案。

回归初衷,创建Service的目的是作为一个远程服务进程,为我们的业务模块提供数据支持,那么这10个业务模块的运程逻辑我们可以让它们运行在用一个Service的进程中,然后通过Binder连接池来管理各自模块的binder。每个模块根据自身的需要得到对应的binder对象,来调用相应的远程服务。

代码实现

aidl

IBinderPool.aidl

// IBinderPool.aidl
package com.stone.templateapp.demo.binderpool;

// Declare any non-default types here with import statements

interface IBinderPool {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    IBinder queryBinder(int binderCode);
}

ICompute.aidl

// ICompute.aidl
package com.stone.templateapp.demo.binderpool;

// Declare any non-default types here with import statements

interface ICompute {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    int add(int a, int b);
}

ISecurityCenter.aidl

// ISecurityCenter.aidl
package com.stone.templateapp.demo.binderpool;

// Declare any non-default types here with import statements

interface ISecurityCenter {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
    String encrypt(String content);
    String decrypt(String password);
}

说明一下:这里我们通过 ICompute 与 ISecurityCenter 来代表多个业务模块的通信需求。两个足够说明逻辑流程了,再多的模块根据自身需求同理增加即可。

IBinderPool.aidl 是Binder连接池对外提供获取binder对象的接口,然后我们只需要创建一个 IBinderPool对应的Service即可,各个业务模块通过Binder连接池提供的公开接口来获取各自对应的binder对象。

服务端

BinderPoolService.kt 连接池的服务

class BinderPoolService : Service() {
    //Binder连接池 服务,统一向各个模块提供跨进程通信的Binder
    private val mBinderPool = BinderPool.BinderPoolImpl()

    override fun onBind(intent: Intent?): IBinder {
        println("on bind")
        return mBinderPool
    }
}

BinderPool.kt 连接池的具体实现逻辑

class BinderPool private constructor(context: Context) {
    private val ctx: Context = context.applicationContext

    private lateinit var mConnectBinderPoolCountDownLatch: CountDownLatch
    private var mBinderPool: IBinderPool? = null

    companion object {
        private const val TAG = "BinderPool"
        const val BINDER_COMPUTE = 0
        const val BINDER_SECURITY_CENTER = 1

        @SuppressLint("StaticFieldLeak")
        @Volatile
        private var sInstance: BinderPool? = null

        /**
         * 懒汉式单例来处理BinderPool的对象获取
         */
        fun getInstance(context: Context): BinderPool {
            if (sInstance == null) {
                synchronized(BinderPool::class.java) {
                    if (sInstance == null) {
                        sInstance = BinderPool(context)
                    }
                }
            }
            return sInstance!!
        }
    }

    private val mBinderPoolDeathRecipient = object : IBinder.DeathRecipient {
        override fun binderDied() {
            Logs.w(TAG, "binder died.")
            mBinderPool?.asBinder()?.unlinkToDeath(this, 0)
            mBinderPool = null
            connectBinderPoolService()
        }
    }

    private val mBinderPoolConnection = object : ServiceConnection {
        override fun onServiceDisconnected(name: ComponentName?) {
            //do nothing
        }

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mBinderPool = IBinderPool.Stub.asInterface(service)
            try {
                //设置死亡代理
                mBinderPool!!.asBinder().linkToDeath(mBinderPoolDeathRecipient, 0)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
            //连接上服务之后,调用countDown,使得被阻塞的线程继续执行
            mConnectBinderPoolCountDownLatch.countDown()
        }
    }

    init {
        //初始化中 绑定服务
        connectBinderPoolService()
    }


    @Synchronized
    private fun connectBinderPoolService() {
        //可以阻塞线程,在这里用来将异步操作 转换为同步操作
        mConnectBinderPoolCountDownLatch = CountDownLatch(1)
        //具体执行绑定服务
        ctx.bindService(Intent(ctx, BinderPoolService::class.java), mBinderPoolConnection, Context.BIND_AUTO_CREATE)
        try {
            //阻塞当前线程,等待 CountDown 为0。例如初始化中 count==1,那么只需要有一次的调用 countDown 方法,那么此处被阻塞的线程就会被唤醒 并继续执行
            mConnectBinderPoolCountDownLatch.await()
        } catch (e: InterruptedException) {
            e.printStackTrace()
        }
    }

    fun queryBinder(binderCode: Int): IBinder? {
        return try {
            //开放给外部,获取各自模块的binder
            mBinderPool?.queryBinder(binderCode)
        } catch (e: Exception) {
            null
        }
    }


    class BinderPoolImpl : IBinderPool.Stub() {

        @Throws(RemoteException::class)
        override fun queryBinder(binderCode: Int): IBinder? {
            //Binder池中具体生成模块各自的Binder
            return when (binderCode) {
                BINDER_COMPUTE -> ComputeImpl()
                BINDER_SECURITY_CENTER -> SecurityCenterImpl()
                else -> null
            }
        }
    }
}

ComputeImpl 计算模块的Binder类

class ComputeImpl : ICompute.Stub() {
    //计算模块的Binder类
    override fun add(a: Int, b: Int): Int {
        println("ComputeImpl invoke add")
        return a + b
    }
}

SecurityCenterImpl 加解密模块的Binder类

class SecurityCenterImpl : ISecurityCenter.Stub() {
    //加解密模块的Binder类
    companion object {
        const val SECRET_CODE = '^'.toInt()
    }

    override fun encrypt(content: String): String {
        println("SecurityCenterImpl invoke encrypt $content")
        val chars = content.toCharArray()
        for ((i, char) in chars.withIndex()) {
            //按位异或
            chars[i] = (char.toInt() xor SECRET_CODE).toChar()
        }
        return String(chars)
    }

    override fun decrypt(password: String): String {
        return encrypt(password)
    }
}

客户端

class BinderPoolActivity : AppCompatActivity() {
    companion object {
        const val TAG = "BinderPoolActivity"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_binder_pool)
        setTitle(R.string.title_binder_pool)
        //由于绑定服务时,做了阻塞操作,所以这里需要在线程中执行。
        Thread(Runnable { doWork() }).start()
    }

    /**
     * 模拟各模块的调用
     */
    private fun doWork() {
        val binderPool = BinderPool.getInstance(act)
        val securityBinder = binderPool.queryBinder(BinderPool.BINDER_SECURITY_CENTER)
        val mSecurityCenter = ISecurityCenter.Stub.asInterface(securityBinder)
        Logs.d(TAG, "visit ISecurityCenter")
        val msg = "helloWorld-安卓"
        println("content: $msg")
        try {
            val pwd = mSecurityCenter.encrypt(msg)
            println("encrypt: $pwd")
            println("decrypt: ${mSecurityCenter.decrypt(pwd)}")
        } catch (e: RemoteException) {
            e.printStackTrace()
        }

        Logs.d(TAG, "visit ICompute")
        val mComputeBinder = binderPool.queryBinder(BinderPool.BINDER_COMPUTE)
        val mCompute = ICompute.Stub.asInterface(mComputeBinder)
        try {
            println("3 + 5 = ${mCompute.add(3, 5)}")
        } catch (e: RemoteException) {
            e.printStackTrace()
        }

        Logs.d(TAG, "the work is finished")
    }
}

AndroidManifest

        <service
            android:name=".demo.binderpool.BinderPoolService"
            android:process=":binder_pool" />
        <activity android:name=".demo.binderpool.BinderPoolActivity" />

运行结果

com.stone.testdemo:binder_pool I/System.out: on bind
com.stone.testdemo D/BinderPoolActivity: [ Thread-5577: (BinderPoolActivity.kt:30) doWork ] - visit ISecurityCenter
com.stone.testdemo I/System.out: content: helloWorld-安卓
com.stone.testdemo:binder_pool I/System.out: SecurityCenterImpl invoke encrypt helloWorld-安卓
com.stone.testdemo I/System.out: encrypt: 6;221 1,2:s寗匍
com.stone.testdemo:binder_pool I/System.out: SecurityCenterImpl invoke encrypt 6;221    1,2:s寗匍
com.stone.testdemo I/System.out: decrypt: helloWorld-安卓
com.stone.testdemo D/BinderPoolActivity: [ Thread-5577: (BinderPoolActivity.kt:41) doWork ] - visit ICompute
com.stone.testdemo:binder_pool I/System.out: ComputeImpl invoke add
com.stone.testdemo I/System.out: 3 + 5 = 8
com.stone.testdemo D/BinderPoolActivity: [ Thread-5577: (BinderPoolActivity.kt:50) doWork ] - the work is finished

结尾

完成上述封装之后,当有新业务需要增加AIDL的时候,我们只需要实现自己的 AIDL 接口之后,修改一下 queryBinder 方法,并增加一个新的 binderCode 即可。并不需要创建新的 Service。

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

推荐阅读更多精彩内容