参考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。
网友评论