基于Binder的4种RPC调用

基于Binder的4种RPC调用 - Wesley's Blog

基于Binder的4种RPC调用一般有AIDL、Messenger、借助 intent 传递 messenger 和 基于 contentprovider 的 call

AIDL 和Messenger 比较常见了,可以参考:绑定服务概览 | Background work | Android Developers

这里只讨论后面两种情况,代码链接:github.com/wesley666/B...

借助 intent 传递 messenger

应用场景:比如授权弹窗需要借助另外一个进程或者组件完成,比如小米 adb 安装应用时的弹窗,安装流程是 system_server 完成的,但安全校验弹窗是安全中心负责的,方便解耦。

kotlin 复制代码
class Test {
    companion object {
        private const val TAG = "TestIntentRPC"
    }
​
    fun testServer(context: Context) {
        val messenger = TestMessenger()
        val intent = Intent()
        intent.`package` = context.packageName
        intent.action = "android.intent.action.AUTHORIZE"
        val bundle = Bundle().apply {
            putValue(MESSENGER_KEY, messenger)
        }
        intent.apply {
            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
            putExtra(ACTIVITY_INTENT_KEY, bundle)
        }
        context.startActivity(intent)
        synchronized(messenger.lock) {
            while (!messenger.finished) {
                try {
                    messenger.lock.wait(10 * 1000L)
                    messenger.finished = true
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }
            }
        }
        Log.e(TAG, "testServer messenger.result: ${messenger.result}")
    }
​
    fun testClient(intent: Intent?) {
        var iBinder: IBinder? = null
        var messenger: IMessenger? = null
        intent?.getBundleExtra(ACTIVITY_INTENT_KEY)?.apply {
            iBinder = getBinderValue(MESSENGER_KEY)
        }
        iBinder?.let {
            messenger = IMessenger.Stub.asInterface(it)
        }
        messenger?.let {
            val message = Message()
            message.what = 99
            it.send(message)
        }
​
    }
​
    private inner class TestMessenger : IMessenger.Stub() {
        @Volatile
        var finished: Boolean = false
        var result: Int = 0
        val lock = Object()
​
        @SuppressLint("RethrowRemoteException", "VisiblySynchronized", "MissingNullability")
        @Throws(
            RemoteException::class
        )
        override fun send(message: Message) {
            synchronized(lock) {
                Log.i(TAG, "onAuthCheck send result: ${message.what}")
                this.finished = true
                this.result = message.what
                lock.notifyAll()
            }
        }
    }
}

framework 的代码可以借助IMessenger.Stub,app的代码可以自定义IMessenger

kotlin 复制代码
@SuppressLint({"RawAidl", "CallbackMethodName", "MissingNullability", "RethrowRemoteException"})
    public static class TestCallback extends IMessenger.Stub {
        boolean finished;
        String msg;
        int result;
​
        @SuppressLint({"RethrowRemoteException", "VisiblySynchronized", "MissingNullability"})
        public void send(@NonNull Message message) throws RemoteException {
            synchronized (this) {
                this.finished = true;
                this.result = message.what;
                Bundle data = message.getData();
                if (data != null) {
                    this.msg = data.getString("msg");
                }
                notifyAll();
            }
        }
    }

基于 contentprovider 的 call 方法

应用场景:比如需要同步 RPC 调用,aidl 是异步绑定的;新增或者删减方法更加方便,aidl 需要严格的方法定义顺序。

比如Settings.Secure.putInt()本质上就是利用了call方法进行rpc,可以参考:

frameworks/base/core/java/android/provider/Settings.java

frameworks/base/packages/SettingsProvider/src/com/android/providers/settings/SettingsProvider.java

定义服务端:contentprovider,call里面也可以增加鉴权等

kotlin 复制代码
    override fun call(method: String, arg: String?, extras: Bundle?): Bundle? {
        if (ProviderHelper.CALL_FUNCTION == method && extras != null && arg != null) {
            return funMap.callFun(arg, extras)
        }
        return super.call(method, arg, extras)
    }

服务端方法定义

kotlin 复制代码
class FunMap(private val context: Context) {
    private val functions: HashMap<String, (Bundle) -> Bundle?> = HashMap()
​
    init {
        init()
    }
​
    private fun init() {
        functions[MethodDef.SAY_HELLO] = { bundle ->
            Log.i(TAG, "say hello from ${bundle.getString(ProviderHelper.KEY_ONE)}")
            null
        }
        functions[MethodDef.GET_NAME] = lambda@{
            return@lambda Bundle().apply {
                putString(ProviderHelper.KEY_RET, context.packageName)
            }
        }
    }
​
​
    fun callFun(methodName: String, bundle: Bundle): Bundle {
        return functions[methodName]?.invoke(bundle) ?: Bundle()
    }
​
​
    companion object {
        private const val TAG = "ProviderFunMap"
    }
}

公共方法

kotlin 复制代码
object MethodDef {
    const val SAY_HELLO = "sayHello"
    const val GET_NAME = "getName"
}
​
@SuppressLint("StaticFieldLeak")
class ProviderHelper(private val context: Context, private val uriPath: String) {
    companion object {
        const val KEY_ONE: String = "key_one"
        const val KEY_TWO: String = "key_two"
        const val KEY_THREE: String = "key_three"
        const val KEY_FOUR: String = "key_four"
        const val KEY_FIVE: String = "key_five"
        const val KEY_RET: String = "ret"
        const val CALL_FUNCTION: String = "callFunction"
        const val TAG: String = "ProviderHelper"
    }
​
    private val mContentResolver: ContentResolver? = context.contentResolver
​
​
    fun callFunction(methodName: String, bundle: Bundle): Bundle? {
        val client = getClient() ?: return Bundle().apply {
            Log.e(TAG, "callFunction $methodName: client is null")
        }
        return try {
            client.call(CALL_FUNCTION, methodName, bundle)
        } catch (e: Exception) {
            Bundle()
        } finally {
            //如果频繁调用,可独立成一个函数,避免每次都close
            client.release()
        }
    }
​
    fun setParams(
        obj: Any? = null,
        obj2: Any? = null,
        obj3: Any? = null,
        obj4: Any? = null,
        obj5: Any? = null
    ): Bundle {
        val bundle = Bundle()
        putValue(bundle, KEY_ONE, obj)
        putValue(bundle, KEY_TWO, obj2)
        putValue(bundle, KEY_THREE, obj3)
        putValue(bundle, KEY_FOUR, obj4)
        putValue(bundle, KEY_FIVE, obj5)
        return bundle
    }
​
    private fun putValue(bundle: Bundle, key: String, obj: Any?) {
        if (obj == null) {
            return
        }
        bundle.putValue(key, obj)
    }
​
    private fun getClient(): ContentProviderClient? {
        val contentResolver = this.mContentResolver ?: return null
        return contentResolver.acquireUnstableContentProviderClient(Uri.parse(this.uriPath))
    }
}

客户端

kotlin 复制代码
class TestProviderRPC(private val context: Context) {
    private val providerHelper =
        ProviderHelper(context, "content://${context.packageName}.rpc.provider")
​
    fun sayHello(str: String) {
        providerHelper.callFunction(MethodDef.SAY_HELLO, providerHelper.setParams(str))
    }
​
    fun getName(): String {
        val ret = providerHelper.callFunction(MethodDef.GET_NAME, providerHelper.setParams())
        return ret?.getString(ProviderHelper.KEY_RET) ?: ""
    }
}
相关推荐
2501_9151063238 分钟前
iOS 打包 IPA 全流程详解,签名配置、工具选择与跨平台上传实战指南
android·macos·ios·小程序·uni-app·cocoa·iphone
超低空43 分钟前
Android MediaSession深度解析:车载音乐播放器完整案例
android·架构·客户端
QmDeve44 分钟前
Android 集成与使用模糊开关按钮视图 (BlurSwitchButtonView)
android·github
00后程序员张44 分钟前
iOS 混淆实操指南多工具组合实现 IPA 混淆、加固与发布治理 IPA 加固
android·ios·小程序·https·uni-app·iphone·webview
xiaoshiquan12062 小时前
as强制过滤指定依赖版本库,解决该依赖不同版本冲突
android
2501_929157683 小时前
Switch 20.5.0系统最新PSP模拟器懒人包
android·游戏·ios·pdf
用户095 小时前
Kotlin Flow的6个必知高阶技巧
android·面试·kotlin
用户095 小时前
Flutter插件与包的本质差异
android·flutter·面试
用户095 小时前
Jetpack Compose静态与动态CompositionLocal深度解析
android·面试·kotlin
聆风吟º8 小时前
【Spring Boot 报错已解决】别让端口配置卡壳!Spring Boot “Binding to target failed” 报错解决思路
android·java·spring boot