• Android Aidl跨进程通讯(四)--接口回调,服务端向客户端发送数据


    学更好的别人,

    做更好的自己。

    ——《微卡智享》

    6659c49042d964837f3a56d16073fb65.jpeg

    本文长度为3325,预计阅读9分钟

    前言

    前几篇介绍了AIDL通讯的基础,进阶和异常捕获,本篇就来看看服务端怎么向客户端来实现发送消息。

    实现服务端往客户端发送消息,主要还是通过接口回调的方式来实现,服务端主要通过RemoteCallbackList注册及解绑监听。

    fffc97c64996a3caf38dd5b0d3fb9281.png

    实现效果

    c617e0e31946b3435d542ddf29bed93f.gif

    7747157784f5d78ee4f5afe1405d8e12.png

    接口回调实现

    8935d949d0ee2d27811339073bd40ac4.png

    微卡智享

    #实现步骤
    1服务端创建接口回调的AIDL
    2通过RemoteCallbackList注册客户端的监听
    3客户端拷贝创建的AIDL
    4客户端写回调实现,注册到服务端

    还是使用上几篇延用下来的Demo

    服务端实现

    7e23c4ef1897018470ce73efa98c4a1b.png

    在服务端创建一个IServiceListener的AIDL文件,里面写一个方法为calback,参数是String类型

    1. // IServiceListener.aidl
    2. package vac.test.aidlservice;
    3. // Declare any non-default types here with import statements
    4. interface IServiceListener {
    5. void callback(String msg);
    6. }

    c106dac96a859befcecd70d99bb4eb21.png

    然后在原来的ITestDataAidlInterface.Aidl中首先要引入刚刚创建的IServiceListener

    86daa7f231775a411c651f331645d670.png

    接着在下面加入两个方法,一个注册监听,一个解绑监听,这两个方法前面加上了oneway的修饰,使IPC调用变成非阻塞的,oneway在上一篇中有简单介绍过。

    1. // ITestDataAidlInterface.aidl
    2. package vac.test.aidlservice;
    3. // Declare any non-default types here with import statements
    4. import vac.test.aidlservice.IServiceListener;
    5. interface ITestDataAidlInterface { Bundle bundle);
    6. //注册监听
    7. oneway void registerListener(IServiceListener listener);
    8. //解绑监听
    9. oneway void unregisterListener(IServiceListener listener);
    10. }

    AIDL这样就算写好了,然后我们重新Rebuild一下项目后,需要在Service中加上这两个方法的实现。

    8e8b23e935b518207f5d86bd1a914f7c.png

    在AidlService中定义一个RemoteCallbackList

    83f8c66d2786d0c7940bdd4f52cc42a2.png

    注册和解绑里面直接通过RemoteCallbackList中的register和unregister实现。

    RemoteCallbackList用于管理一组已注册的IInterface回调,并在它们的进程消失时自动从列表中清理它们。RemoteCallbackList通常用于执行从Service到其客户端的回调,实现跨进程通信。其特点主要是

    • 通过调用IInterface.asBinder()方法,根据底层的唯一Binder来识别每个注册的接口。

    • 给每个注册的接口附加了一个IBinder.DeathRecipient,这样如果接口所在的进程死亡了,它就可以从列表中清除掉。

    • 对底层接口列表进行了加锁处理,以应对多线程的并发调用,同时提供了一种线程安全的方式来遍历列表的快照,而不需要持有锁。

    使用RemoteCallbackList先创建一个实例,并调用它的register(E)和unregister(E)方法作为客户端注册和解绑。

    要回调到注册的客户端使用beginBroadcast()、getBroadcastItem(int)和finishBroadcast()方法。在beginBroadcast()后必须要先执行finishBroadcast()后,才可以进行下次的beginBroadcast(),否则会报错beginBroadcast() called while already in a broadcast

    a5572a89695f795d8ce4a6362c966335.png

    然后写了一个sendmsg的方法,这里用到的协程,每3秒服务端发送一次消息。

    4ad602ac6a3dd268954e8fa1ad7061a5.png

    在OnCreate中直接加入发送数据的调用

    2dcfb375053a0803d9a37a42fb80da27.png

    服务的onDestroy中要记得加入RemoteCallbackList的kill()。

    1. package vac.test.aidlservice
    2. import android.app.Notification
    3. import android.app.NotificationChannel
    4. import android.app.NotificationManager
    5. import android.app.Service
    6. import android.content.Context
    7. import android.content.Intent
    8. import android.os.Build
    9. import android.os.Bundle
    10. import android.os.IBinder
    11. import android.os.RemoteCallbackList
    12. import android.os.RemoteException
    13. import android.util.DisplayMetrics
    14. import android.util.Log
    15. import androidx.annotation.RequiresApi
    16. import kotlinx.coroutines.Dispatchers
    17. import kotlinx.coroutines.GlobalScope
    18. import kotlinx.coroutines.delay
    19. import kotlinx.coroutines.launch
    20. import kotlinx.coroutines.sync.Mutex
    21. import kotlinx.coroutines.sync.withLock
    22. import java.io.IOException
    23. import kotlin.jvm.Throws
    24. class AidlService : Service() {
    25. val CHANNEL_STRING = "vac.test.aidlservice"
    26. val CHANNEL_ID = 0x11
    27. val mTestDatas: MutableList = mutableListOf()
    28. //监听集合 用于管理一组已注册的IInterface回调
    29. private val mCallBackList: RemoteCallbackList = RemoteCallbackList()
    30. private var testBinder = object : ITestDataAidlInterface.Stub() {
    31. override fun basicTypes(
    32. anInt: Int,
    33. aLong: Long,
    34. aBoolean: Boolean,
    35. aFloat: Float,
    36. aDouble: Double,
    37. aString: String?
    38. ) {
    39. TODO("Not yet implemented")
    40. }
    41. override fun getTestData(code: String?): TestData? {
    42. // return mTestDatas.firstOrNull { t -> t.code == code }
    43. throw SecurityException("我是AidlService进程中的异常,你看到了吗?")
    44. }
    45. override fun getTestDatas(): MutableList {
    46. return mTestDatas
    47. }
    48. override fun updateTestData(data: TestData?): Boolean {
    49. data?.let {
    50. var item: TestData? =
    51. mTestDatas.firstOrNull { t -> t.code == it.code } ?: return false
    52. item?.let { t ->
    53. t.code = it.code
    54. t.name = it.name
    55. t.price = it.price
    56. t.qty = it.qty
    57. }
    58. return true
    59. } ?: return false
    60. }
    61. override fun updateTestDatsList(datas: MutableList<TestData>?): Boolean {
    62. datas?.let {
    63. val item = TestData("99999", "我是新加数据", 1.0f, 1)
    64. it.add(item)
    65. mTestDatas.addAll(it)
    66. it.clear()
    67. it.addAll(mTestDatas)
    68. return true
    69. } ?: return false
    70. }
    71. override fun transBundle(bundle: Bundle?): MutableList {
    72. bundle?.let { it ->
    73. /*
    74. Android有两种不同的classloaders:framework classloader和apk classloader,
    75. 其中framework classloader知道怎么加载android classes,
    76. apk classloader继承自framework classloader,所以也知道怎么加载android classes。
    77. 但在应用刚启动时,默认class loader是apk classloader,在系统内存不足应用被系统回收会再次启动,
    78. 这个默认class loader会变为framework classloader了,所以对于自己的类会报ClassNotFoundException
    79. 就会出现android.os.BadParcelableException: ClassNotFoundException when unmarshalling
    80. */
    81. //所以在bundle数据读取前,先设置classloader后,才能正确的读取自定义类
    82. it.classLoader = TestData::class.java.classLoader
    83. val price = it.getFloat("price")
    84. val qty = it.getInt("qty")
    85. mTestDatas.map { t ->
    86. t.price = price
    87. t.qty = qty
    88. }
    89. val list = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    90. it.getParcelableArrayList("listdatas", TestData::class.java)
    91. } else {
    92. it.getParcelableArrayList("listdatas")
    93. }
    94. list?.let { item ->
    95. mTestDatas.addAll(item)
    96. }
    97. }
    98. return mTestDatas
    99. }
    100. //注册监听
    101. override fun registerListener(listener: IServiceListener?) {
    102. mCallBackList.register(listener);
    103. }
    104. //解绑监听
    105. override fun unregisterListener(listener: IServiceListener?) {
    106. mCallBackList.unregister(listener);
    107. }
    108. }
    109. fun initList() {
    110. for (i in 1..5) {
    111. val price = ((0..10).random()).toFloat()
    112. val qty = (10..50).random()
    113. val item = TestData("0000${i}", "测试数据${i}", price, qty)
    114. mTestDatas.add(item)
    115. }
    116. }
    117. fun sendmsg() {
    118. GlobalScope.launch(Dispatchers.Default) {
    119. delay(3000)
    120. repeat(10) { i ->
    121. delay(3000)
    122. val mutex = Mutex()
    123. mutex.withLock {
    124. val size = mCallBackList.beginBroadcast()
    125. Log.i("aidlpkg", "mCallBackList:${size}")
    126. try {
    127. val msg = "服务端发的第${i}次消息"
    128. Log.i("aidlpkg", "sendMsgtoClient:${msg}")
    129. for (i in 0 until size) {
    130. mCallBackList.getBroadcastItem(i).callback(msg)
    131. }
    132. mCallBackList.finishBroadcast()
    133. } catch (e: IllegalStateException) {
    134. Log.e("aidlpkg", e.message.toString())
    135. mCallBackList.finishBroadcast()
    136. throw RemoteException(e.message)
    137. }
    138. }
    139. }
    140. }
    141. }
    142. fun startServiceForeground() {
    143. val notificationManager =
    144. getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
    145. val channel = NotificationChannel(
    146. CHANNEL_STRING, "AidlServer",
    147. NotificationManager.IMPORTANCE_LOW
    148. )
    149. notificationManager.createNotificationChannel(channel)
    150. val notification = Notification.Builder(applicationContext, CHANNEL_STRING).build()
    151. startForeground(CHANNEL_ID, notification)
    152. }
    153. override fun onCreate() {
    154. super.onCreate()
    155. /*Debug版本时调试使用 */
    156. // Debug.waitForDebugger()
    157. startServiceForeground()
    158. //初始化数据
    159. initList()
    160. //开始发送返回消息
    161. sendmsg()
    162. }
    163. override fun onBind(intent: Intent): IBinder {
    164. return testBinder
    165. }
    166. override fun onDestroy() {
    167. super.onDestroy()
    168. mCallBackList.kill()
    169. }
    170. }

    客户端实现

    067107c6c8d11be032fbd5d5d7eecefa.png

    客户端首先也要将服务端已经写好的两个aidl文件拷贝过来

    2c8bab243cc56472579d1e20da863470.png

    然后在客户端MainActivity中定义IServiceListener.Stub的实现,这里是收到了消息后直接用Snake弹窗显示出来。

    189d91c27fa5c4d6206de131667a0ec6.png

    当bindService成功后,我们就直接调用注册监听,这里的协程加入了延时1秒,主要是服务端在onBind开启服务的时候有个时间过程,如果不加入延时直接注册,有可能服务端的Service还没启动起来,所以注册不上。

    42d6796fd3b7d210700c2a0acb2f6a9e.png

    onDestory中加入解绑回调,这样我们的MainActivity中关闭后,服务端的RemoteCallbackList也会解绑不再发送数据。


    这样我们就可以实现服务端直接向客户端发送数据了,Demo源码中也已经更新上传了。

    源码地址

    https://github.com/Vaccae/AndroidAIDLDemo.git

    点击原文链接可以看到“码云”的源码地址

    2b03a72188de1a58e73a37009940bf27.png

    4b1d31af76d4f8f269969ff129e4e5c5.png

    往期精彩回顾

     

    11bccb7218a90f193b1b6a0561e795a2.jpeg

    Android Aidl跨进程通讯(三)--进阶使用

     

     

    128d0162715478aee2e5472e40371692.jpeg

    Android Aidl跨进程通讯(二)--异常捕获处理

     

     

    183a2b887fa844642a59bb52f840f0ec.jpeg

    Android Aidl跨进程通讯的简单使用

     

  • 相关阅读:
    Java 编程性能调优
    宝宝为什么会出现乳糖不耐受?
    vue(二)
    在前台页面中console怎么查vue.prototype
    6.19作业
    成兴光 | LED灯珠的封装形式
    linux gdb调试
    5-2传输层-UDP协议
    2020长三角(上海)区块链应用创新大赛复赛评审圆满结束
    回馈负载的工作原理
  • 原文地址:https://blog.csdn.net/Vaccae/article/details/132820269