• Binder进程通信基础使用




    Binder 进程通信基础使用

    一、服务端进程创建 Service,Service 中创建 Binder 子类对象并于 onBind 中返回。xml 定义。
    1. 创建 Service,创建 Binder 子类对象并于 onBind 返回
    class UserService : Service() {
    
        private companion object {
            const val TAG: String = "test_service_tag"
        }
    
        private val service = object : Binder() {
            override fun onTransact(p0: Int, p1: Parcel, p2: Parcel?, p3: Int): Boolean {
                p1.enforceInterface("UserService")
                when(p0) {
                    0 -> {
                        // 读取客户端传递的 int 值
                        val readInt = p1.readInt()
                        Log.e(TAG, "service get int : $readInt")
                        // 写入无异常,表示这次调用没有出现问题
                        p2?.writeNoException()
                        p2?.writeInt(readInt * 2)
                        return true
                    }
                    1 -> {
                        // 读取客户端传递的 String 值
                        val readString = p1.readString()
                        Log.e(TAG, "service get String: $readString")
                        // 写入无异常,表示这次调用没有出现问题
                        p2?.writeNoException()
                        p2?.writeString("service version: 1.0.0")
                        return true
                    }
                    // 2的时候传的 Binder,拿到客户端 Binder 模拟跨进程回调,也就是此时随时可以双向通信
                    2 -> {
                        // 读取客户端传递的 Binder 对象,双向通信,直接点说就是callback回调对象
                        val readStrongBinder = p1.readStrongBinder()
                        Log.e(TAG, "service get binder: $readStrongBinder execute method:")
                        val data = Parcel.obtain()
                        val reply = Parcel.obtain()
                        try {
                            //确认 token 令牌
                            data.writeInterfaceToken("UserService")
                            data.writeInt(111)
                            // 回调客户端,保存传来 Binder 后,随时可以回调,这里简易写一下就行
                            readStrongBinder.transact(0 ,data, reply, 0)
                            //这个方法必须要 reply 调了writeException 或 writeNoException,不然直接抛异常,看方法源码就看出来了
                            reply.readException()
                            Log.e(TAG, "service binder call back result: " + reply.readInt())
                        } finally {
                            data.recycle()
                            reply.recycle()
                        }
                        return true
                    }
                    else -> {
                        Log.d(TAG, "service unspecific value")
                    }
                }
                return super.onTransact(p0, p1, p2, p3)
            }
        }
    
        override fun onBind(p0: Intent?): IBinder {
            Log.e(TAG, "service onBind Service ")
            return service
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63

    简易说明:onTransact(p0: Int, p1: Parcel, p2: Parcel?, p3: Int)

    客户端调用传过去的 Binder.transact 方法时会回调此方法
    p0: requestCode 请求码,可以用于区分不同的执行操作,类似 aidl 中不同的方法的区分
    p1: data 数据,客户端传来的数据,客户端 write 写入,服务端 read 读取。
    p2: reply 数据,当 p3 参数是 0时,在服务端进行 write,客户端调完方法直接 read 读取写入的值。
    p3: flag 标记,0 表示这个方法有返回值,可对 reply 进行写入,然后客户端读取,1 的话表示单向,数据只从客户端到服务端,没有返回值,就算对 reply 写值,客户端也读不到。

    1. 在服务端 AndroidManifest.xml 里定义 service.
            <service android:name=".UserService" android:exported="true"
                android:enabled="true"
                android:permission="com.example.service">
                <intent-filter>
                    <action android:name="com.example.service.UserService" />
                intent-filter>
            service>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    简易说明:exported 外部调用开关,permission 权限限制,不设置不一定能绑定上

    二、客户端绑定服务,通过返回的 Binder 执行逻辑,传递 Binder 完成跨进程回调。xml 权限及包名定义。
    1. 客户端 AndroidManifest.xml 中设定权限、包名.
        <permission android:name="com.example.service" />
        <uses-permission android:name="com.example.service" />
        <queries>
            <package android:name="com.example.service" />
        queries>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    简易说明::TargetSDK > 30之后绑定非自身应用的 service 要加,里面写绑定服务的包名.
    : 在服务端对 service 要求需要该权限,所以要加。

    1. 客户端绑定服务。
        private var service: IBinder? = null//这个代码是定义的 class成员变量,比较懒,就仍这儿了
    
    
            // 绑定服务不能只要 action,会抛异常,看了下实现,最少得加个包名
            val intent = Intent("com.example.service.UserService").also {
                it.component = ComponentName("com.example.service", "com.example.service.UserService")
            }
            Log.e(
                TAG,
                " activity bind service result : " + bindService(intent, object : ServiceConnection {
                    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                        Log.e(TAG, "activity onServiceConnected, service : $service")
                        // 保存服务端传递的 Binder,就是这个
                        this@MainActivity.service = service
                    }
    
                    override fun onServiceDisconnected(name: ComponentName?) {
                        Log.e(TAG, "activity onServiceDisconnected, service : $service")
                        this@MainActivity.service = null
                    }
                    // 最后一个参数要传 Context.BIND_AUTO_CREATE,不然有问题,啥问题可以看 ComtextImpl.bindService 的源码,里面有 flags 参数校验
                }, Context.BIND_AUTO_CREATE)
            )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    简易说明:就俩注意点
    (1)intent 不止传 action,class 和 component 还得加上这两个其中的一个.
    (2)binderService 最后一个参数 flag 需要传 Context.BIND_AUTO_CREATE.

    1. 调用 transact 完成跨进程通信。
        private fun getServiceInfo() {
    
            Log.d(TAG, "activity getServiceInfo, service : $service")
            val data0 = Parcel.obtain()
            val reply0 = Parcel.obtain()
            val requestCode0 = 0
            try {
                //写入 token 令牌,这里是因为客户端写了验证 token,服务端没写就可以不加
                data0.writeInterfaceToken("UserService")
                data0.writeInt(100)
                service?.transact(requestCode0, data0, reply0, 0)
                //这个方法必须要 reply 调了writeException 或 writeNoException,不然直接抛异常,看方法源码就看出来了
                reply0.readException()
                Log.e(
                    TAG,
                    "activity requestCode : $requestCode0, getServiceInfo :  " + reply0.readInt()
                )
            } finally {
                data0.recycle()
                reply0.recycle()
            }
    
            val data1 = Parcel.obtain()
            val reply1 = Parcel.obtain()
            val requestCode1 = 1
            try {
                data1.writeInterfaceToken("UserService")
                data1.writeString("what is service version?")
                service?.transact(requestCode1, data1, reply1, 0)
                reply1.readException()
                Log.e(
                    TAG,
                    "activity requestCode : $requestCode1, getServiceInfo :  " + reply1.readString()
                )
            } finally {
                data1.recycle()
                reply1.recycle()
            }
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40

    问题点:
    (1)token 验证。当服务端进行 data.enforceInterface(令牌值) token验证时,调用的客户端必须写入 token data.writeInterfaceToken(令牌值)
    (2)默认就有异常。客户端调完 transact() 后,如果调了 reply.readException() 那客户端必须要调用 reply.writeNoException() 或者 reply.writeException()。
    (3)data(p1) 和 reply(p2)。data 是客户端写值,服务端读取,相当于 aidl 方法入参,reply 服务端写入,客户端读取,相当于 aidl 方法返回值。
    (4)回调双向通信。客户端服务端在已连接情况下双方随时可向对方通信,客户端调用的 transact() 中,data(p1) 写入的时候写入 Binder 对象,服务端拿到 Binder 对象后保存,这样双方都保存了对方的 Binder,也就是随时可以通信。直接点说就是常见的 registerCallback 方式,客户端拿着 service,服务端拿着 callback。

    三、记一下。。
    1. Binder 子类对象完成跨进程调用
    2. Binder.transact(p0,p1,p2,p3) 调用时回调 Binder.onTransact(p0,p1,p2,p3),p0 code 区分此次事件该做啥;p1 是传来的参数,可以读取另一边的数据;p2 是传给对方的数据,写入,p3 0时 p2有效,1时p2无效.
    3. Parcel token 验证。transact(p0,p1,p2,p3) 调用方调用前写入,onTransact(p0,p1,p2,p3) 接收方中验证。
    4. Parcel.exception 。transact(p0,p1,p2,p3) 调用方调用后读取,onTransact(p0,p1,p2,p3) 接收方执行时写入。
  • 相关阅读:
    LeetCode 39. Combination Sum【回溯,剪枝】中等
    Numpy.array数组学习笔记(数组结构详解,数组操作方法,数组读取方法,数组计算方法,数组画图方法,数组存储方法)
    【Web】CSS学习笔记之浮动*
    快速排序原理JAVA和Scala实现-函数式编程的简洁演示
    计算机毕业设计Django+Vue.js电影推荐系统 电影用户画像系统 电影可视化 电影大数据 机器学习 深度学习 知识图谱 Hadoop Spark
    阿里内部SpringBoot进阶宝典横空出世,实战源码齐飞
    AF_UNIX和127.0.0.1(AF_INET)回环地址写数据速度对比(二)
    jvm 各个版本支持的参数
    2023最新SSM计算机毕业设计选题大全(附源码+LW)之java拼车平台0k47u
    计算机毕业设计node+vue基于微信小程序的乐团团购系统的设计与实现
  • 原文地址:https://blog.csdn.net/qq_52019658/article/details/132889333