IPC之Binder连接池

2019-12-17  本文已影响0人  钦_79f7

参考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。

上一篇下一篇

猜你喜欢

热点阅读