• AIDL基本使用


    一、大致流程

    1.服务端创建 .aidl 文件
    2.定义接口
    3.build生成相应的java文件
    4.向客户端公开接口(写一个service,重写onBind(),返回Stub类的实现)
    5.复制aidl文件及相应的bean类(如果有的话)到客户端
    6.客户端binderService拿到binder实例调用服务端方法。

    例子:
    1.服务端在src/main下创建aidl文件IPersonManager.aidl

    // IPersonManager.aidl
    package com.lmy.androidutilcode;
    
    import com.lmy.androidutilcode.bean.Person;
    
    interface IPersonManager {
        List<Person> getPersonList();
        boolean addPerson(inout Person person);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2.定义好要传输的对象Person

    class Person(var name: String? = "") : Parcelable {
        constructor(parcel: Parcel) : this(parcel.readString())
    
        override fun toString(): String {
            return "Person(name=$name) hashcode = ${hashCode()}"
        }
    
        override fun writeToParcel(parcel: Parcel, flags: Int) {
            parcel.writeString(name)
        }
    
        fun readFromParcel(parcel: Parcel) {
            this.name = parcel.readString()
        }
    
        override fun describeContents(): Int {
            return 0
        }
    
        companion object CREATOR : Parcelable.Creator<Person> {
            override fun createFromParcel(parcel: Parcel): Person {
                return Person(parcel)
            }
    
            override fun newArray(size: Int): Array<Person?> {
                return arrayOfNulls(size)
            }
        }
    
    • 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

    3.然后得在aidl的相同目录下也需要声明一下这个Person对象.新建一个Person.aidl

    package com.xfhy.allinone.ipc.aidl;
    
    parcelable Person;
    
    • 1
    • 2
    • 3

    如图:

    在这里插入图片描述
    4.rebuild一下,AS会自动生成如下代码IPersonManager.java。
    在这里插入图片描述
    注意:aidl文件中最好不要用中文注释

    5.服务端像客户端公开接口
    定义一个Service,然后将其process设置成一个新的进程,与主进程区分开(或者像我这里用2个module,即2个application)。模拟跨进程访问,它里面需要实现.aidl生成的接口

    class RemoteService : Service() {
    
        private val mPersonList = mutableListOf<Person?>()
    
        private val mBinder: Binder = object : IPersonManager.Stub() {
            override fun getPersonList(): MutableList<Person?> = mPersonList
    
            override fun addPerson(person: Person?): Boolean {
                return mPersonList.add(person)
            }
        }
    
        override fun onBind(intent: Intent?): IBinder? {
            return mBinder
        }
    
        override fun onCreate() {
            super.onCreate()
            mPersonList.add(Person("Garen"))
            mPersonList.add(Person("Darius"))
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    实现的IPersonManager.Stub是一个Binder,需要通过onBind()返回,客户端需要通过这个Binder来跨进程调用Service这边的服务.

    6.复制aidl文件和相应的bean对象到客户端
    注意:目录结构要完全一样
    在这里插入图片描述

    7.客户端调用

    class AidlActivity : TitleBarActivity() {
    
        companion object {
            const val TAG = "lmy"
        }
    
        private var remoteServer: IPersonManager? = null
    
        private val serviceConnection = object : ServiceConnection {
            override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
                log(TAG, "onServiceConnected")
                //在onServiceConnected调用IPersonManager.Stub.asInterface获取接口类型的实例
                //通过这个实例调用服务端的服务
                remoteServer = IPersonManager.Stub.asInterface(service)
            }
    
            override fun onServiceDisconnected(name: ComponentName?) {
                log(TAG, "onServiceDisconnected")
            }
        }
    
        override fun getThisTitle(): CharSequence {
            return "AIDL"
        }
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.activity_aidl)
    
            btnConnect.setOnClickListener {
                connectService()
            }
            btnGetPerson.setOnClickListener {
                getPerson()
            }
            btnAddPerson.setOnClickListener {
                addPerson()
            }
        }
    
        private fun connectService() {
            val intent = Intent()
            //action 和 package(app的包名)
            intent.action = "可以给服务端的service定义一个action"
            intent.setPackage("服务端包名")
            val bindServiceResult = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
        }
    
        private fun addPerson() {
            //客户端调服务端方法时,需要捕获以下几个异常:
            //RemoteException 异常:
            //DeadObjectException 异常:连接中断时会抛出异常;
            //SecurityException 异常:客户端和服务端中定义的 AIDL 发生冲突时会抛出异常;
            try {
                val addPersonResult = remoteServer?.addPerson(Person("盖伦"))
                log(TAG, "addPerson result = $addPersonResult")
            } catch (e: RemoteException) {
                e.printStackTrace()
            } catch (e: DeadObjectException) {
                e.printStackTrace()
            } catch (e: SecurityException) {
                e.printStackTrace()
            }
        }
    
        private fun getPerson() {
            val personList = remoteServer?.personList
            log(TAG, "person 列表 $personList")
        }
    
        override fun onDestroy() {
            super.onDestroy()
            //最后记得unbindService
            unbindService(serviceConnection)
        }
    }
    
    • 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
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76

    注意:
    如果targetSdk是30,那么需要处理Android 11中的程序包可见性 具体参见: https://developer.android.com/about/versions/11/privacy/package-visibility

    二、in,out,inout关键字

    在上面定义AIDL接口的时候,咱用到了一个关键字in,这个关键是其实是定向tag,是用来指出数据流通的方式。还有2个tag是out和inout,所有的非基本参数都需要一个定向tag来指出数据的流向,基本参数的定向tag默认并且只能是in。

    in方式是可以从客户端向服务端传数据的,out则不行
    out方式是可以从服务端向客户端传数据的,in则不行
    不管服务端是否有修改传过去的对象数据,客户端的对象引用是不会变的,变化的只是客户端的数据,合情合理,跨进程是序列化与反序列化的方式操作数据。

    三、oneway 关键字

    将aidl接口的方法前加上oneway关键字则这个方法是异步调用,不会阻塞调用线程,当客户端这边调用服务端的方法时,如果不需要知道其返回结果,这时使用异步调用可以提高客户端的执行效率。

    验证:我将aidl接口方法定义成oneway的,在服务端AIDL方法实现中加入Thread.sleep(2000)阻塞一下方法调用,然后客户端调用这个方法,查看方法调用的前后时间

    private fun addPersonOneway() {
    log(TAG, “oneway开始时间: ${System.currentTimeMillis()}”)
    remoteServer?.addPersonOneway(Person(“oneway”))
    log(TAG, “oneway结束时间: ${System.currentTimeMillis()}”)
    }

    //日志输出
    //oneway开始时间: 1608858291371
    //oneway结束时间: 1608858291372
    可以看到,客户端调用这个方法时确实是没有被阻塞的.

    四、线程安全

    AIDL的方法是在服务端的Binder线程池中执行的,所以多个客户端同时进行连接且操作数据时可能存在多个线程同时访问的情形.这样的话,我们就需要在服务端AIDL方法中处理多线程同步问题.

  • 相关阅读:
    商标变更的流程介绍有哪些
    TDK | EMC是什么?
    嵌入式分享合集61
    操作系统识别
    bash shell 初体验-尚文网络xUP楠哥
    Windows(二):windows+nginx+openssl本地搭建nginx并配置ssl实现https访问
    idea插件推荐——Bito提高编码效率
    CDN是什么?
    基于ARM的环境参数检测系统设计(Labview+STM32+ZigBee)
    如何创建自己的小程序?
  • 原文地址:https://blog.csdn.net/qq_34512207/article/details/125544671