IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> 【Android】用AIDL实现Server端回调Client端方法(一) -> 正文阅读

[移动开发]【Android】用AIDL实现Server端回调Client端方法(一)

背景

在公司实习的过程中需要用到跨进程的通信,查看项目之前的代码发现是通过aidl绑定的两个进程,但是因为两个进程需要互相收发消息,所以A→B, B←A进行了两次绑定。本着能只绑定一次就不多浪费一次的原则,选择对这个通信方式进行改造。

首先aidl这个东西就不再多赘述,是非常基础的多进程通信方法。

其次通过一次aidl绑定后就可以调用Server端的服务,这一点也不多赘述,因为是aidl的平A用法。

这里主要说明一下如何使用aidl实现反向调用客户端的方法

用法

通过查找其他博主的文章,发现统一使用了RemoteCallbackList<?>() 这种列表,通过客户端将callback注册到这个列表中,在服务端遍历这个列表,便可以调用里面存储的客户端的方法。

下面是具体做法:

  1. 首先书写两个aidl文件,作为服务端和客户端双方希望对方调用的接口:

    //这是服务端提供给客户端调用的接口
    // Myinterface.aidl
    package com.example.crossprocesslistenertest;
    import com.example.crossprocesslistenertest.Anotherlistener;
    
    interface Myinterface {
    //服务端提供的服务
        void doService(int id, out Bundle params);
    //服务端提供给客户端注册回调的方法
        void registerListener(Anotherlistener listener);
    //服务端提供给客户端解注册回调的方法
        void unregisterListener(Anotherlistener listener);
    }
    
    //客户端提供给服务端回调的方法
    // Anotherlistener.aidl
    package com.example.crossprocesslistenertest;
    
    interface Anotherlistener {
        void doListener1(int id, inout Bundle params);
    }
    

    别忘了build一下,否则接口文件不会生成

  2. 创建一个Service,作为被绑定的服务端(别忘了在AndroidMaifest中把这个Service注册在另一个进程)在这里插入图片描述

    //AnotherService,表示在另一个进程
    class AnotherService : Service() {
    
        private val TAG = "remote process"
    
        private val mListener = RemoteCallbackList<Anotherlistener>()
    
    //在这里实现一下Service提供的服务接口
        private val service = object : Myinterface.Stub(){
    //这个接口方法用日志展示一下这个服务被调用
            override fun doService(id : Int, params : Bundle) {
                Log.d(TAG,"the remote service was did")
            }
    //这个接口方法用来注册客户端的回调方法
            override fun registerListener(listener: Anotherlistener?) {
    //这里调用RemoteCallbackList的register方法,可以将泛型方法注册进去
                mListener.register(listener)
            }
    //这个接口方法用来解注册客户端的回调方法
            override fun unregisterListener(listener: Anotherlistener?) {
                mListener.unregister(listener)
            }
        }
    
    //通过这个方法调用主进程中提供的回调方法,主要是通过遍历list中注册的方法
    fun doMainListener(){
    //首先要开启一下“广播”,注意这里并不是四大组件中的广播,这个方法会返回列表中的object数量
        val n =mListener.beginBroadcast()
    //我们可以通过这个日志打印一下具体有几个回调,一般只有一个
        Log.d("AnotherService","${mListener.registeredCallbackCount}")
    //遍历列表中的回调方法
        for (i in 0 until n){
    //调用getBroadcastItem(),通过具体的索引可以获得这个回调对象,注意这个对象类型是我们提供的泛型,也就是说这就是我们要拿到的回调接口!这是一件好事,后面会提到
            mListener.getBroadcastItem(i).doListener1() //因为我们已经通过泛型定义了存储在列表中的的回调接口,我们就可以直接使用它们了(注意这里的doListener是我们在客户端aidl中提供的)
        }
    //调用完成之后,一定要记得调用dinishBBroadcast方法关闭广播
        mListener.finishBroadcast()
    }
    
    //这里就是正常的绑定流程,将service接口返回即可
        override fun onBind(intent: Intent): IBinder? {
            return service.asBinder
        }
    }
    
  3. 接下来在主进程中的MainActivity中书写逻辑:

    class MainActivity : AppCompatActivity() {
    //在这里准备一个service属性,用来获取得到的服务端接口
        private var mService : Myinterface? = null
    
        private val TAG = "main process"
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_main)
    //在onCreate方法中对服务端服务进行绑定
            bind()
    //通过给按钮注册点击事件调用服务端提供的服务
            do_service.setOnClickListener {
                mService?.doService(3, Bundle().apply {
                    putString("key","11111")
                })
            }
        }
    
    //实现客户端的aidl接口,也就是希望服务端回调的方法
        private val listener = object : Anotherlistener.Stub(){
            override fun doListener1(id : Int, params : Bundle) {
    //同样通过日志表明这个方法是否有被调用
                Log.d(TAG, "the main process listener1111 was called")
            }
        }
    
    //bind service必备的connection属性
        private val connection = object : ServiceConnection{
    //在和服务建立连接的回调中,注册自己的回调(上面的listener)
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
    //首先拿到service端的服务接口
                mService = Myinterface.Stub.asInterface(service)
    //调用服务端的注册listener方法,并将自己的接口回调注册进去
                mService?.registerListener(listener)
                Log.d(TAG, "Connection success!")
            }
    
            override fun onServiceDisconnected(name: ComponentName?) {
                mService = null
            }
    
        }
    
    //绑定服务端的方法
        private fun bind(){
            bindService(Intent(this, AnotherService::class.java).setType("MAIN"), connection, BIND_AUTO_CREATE)
        }
    //解绑方法
        private fun unbind(){
            if(mService?.asBinder()?.isBinderAlive == true){
                mService?.unregisterListener(listener)
                unbindService(connection)
            }
        }
    
    //这里不再赘述
        override fun onDestroy() {
    
            if (mService?.asBinder()?.isBinderAlive == true){
    //从list中解注册,但实际上好像不需要,在连接断开后,列表中的回调会自动清除
                mService?.unregisterListener(listener)
            }
            unbindService(connection)
            super.onDestroy()
        }
    }
    
  4. 尝试一下客户端使用Web端的服务:(每次点击按钮都会打印)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RZRTtv9X-1627653981041)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/defd5b4f-02da-4ed6-bc16-893bbf85be38/Untitled.png)]

  5. 目前服务端是一个Service,没有View,只能添加一个线程帮我们去触发这个操作:

    //在AnotherService中添加:
    override fun onCreate() {
        super.onCreate()
        Thread(doSth).start()
    }
    
    //这个方法会创建一个Runnable,我们通过lambda重写里面的run方法
    private val doSth = Runnable {
    //每十秒调用一次客户端的回调
        for (i in 1 .. 10){
            Thread.sleep(1000)
    				doMainListener()
        }
    }
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N5lEA6DX-1627653981042)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/7f9d9b76-c848-44ad-8552-ef8895b3655e/Untitled.png)]
    以上省略6次

以上实现了AIDL中Server端回调Client端的方法,但是如果每次调用callback的时候,都需要对RemoteCallbackList 进行开启→遍历→关闭,是不是有点费事?如果我们的的回调接口中有很多个方法,而这些方法在服务端被异步调用,就会存在RemoteCallbackList的开启问题,因为RemoteCallbackList的所有方法是被Synchronize修饰的,那么异步调用就会被强制变成同步过程,降低了效率。

那么为了解决上述的问题,在下一篇文章中会尝试将RemoteCallbackList中的回调方法取出,并直接使用

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-08-01 14:37:31  更:2021-08-01 14:39:33 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/7 3:05:54-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码