Binder

Binder机制优点:

  • 只需要进行一次数据拷贝,性能上仅次于共享内存。
  • 基于C/S架构,职责明确,架构清晰,稳定性较好。
  • 安全性好,为每个App分配UID,UID是鉴别进程身份的标志。

内存映射:(一次copy的原因)

Binder IPC 机制中涉及到的内存映射通过 mmap() 来实现,mmap() 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。

Binder IPC 通信过程:

  • 首先 Binder 驱动在内核空间创建一个数据接收缓存区;
  • 接着在内核空间开辟一块内核缓存区,建立内核缓存区和内核中数据接收缓存区之间的映射关系,以及内核中数据接收缓存区和接收进程用户空间地址的映射关系;
  • 发送方进程通过系统调用 copy_from_user() 将数据 copy 到内核中的内核缓存区,内核缓存区映射到接收区,接收区又映射到用户空间地址,因此也就相当于把数据发送到了接收进程的用户空间,这样便完成了一次进程间的通信。

Binder通讯模型:
Binder是基于C/S架构的,包含4个角色:Client、Server、Binder驱动和ServiceManager。

  • Binder驱动:类似网络通信中的路由器,负责将Client的请求转发到具体的Server中执行,并将Server返回的数据传回给Client。
  • ServiceManager:类似网络通信中的DNS服务器,负责将Client请求的Binder描述符转化为具体的Server地址,以便Binder驱动能够转发给具体的Server。Server如需提供Binder服务,需要向ServiceManager注册。

通信过程:

  1. Server向ServiceManager注册。Server通过Binder驱动向ServiceManager注册,声明可以对外提供服务。ServiceManager中会保留一份映射表。
  2. Client向ServiceManager请求Server的Binder引用。Client想要请求Server的数据时,需要先通过Binder驱动向ServiceManager请求Server的Binder引用(代理对象),ServiceManager根据Server服务名找到对应的Server,然后向具体的Server发送请求。
  3. 创建远程代理对象。Server响应请求后,通过Binder驱动将结果返回给Client。Client在收到该Server的Binder引用信息之后,就根据该Binder引用信息创建一个Server对应的远程代理对象,Client通过调用该远程服务的接口,就相当于在调用Server的服务接口一样。

Client和Server通信:

Client要和Server通信,它就是通过保存一个Server对象的Binder引用,再通过该Binder引用在内核中找到对应的Binder实体,进而找到Server对象,然后将通信内容发送给Server对象。

Client进程将需要传送的数据写入到Parcel对象中调用BinderProxy的transact()将上述数据发送到Binder驱动(通过BpBinder)Binder驱动找到Binder引用对应的Binder实体,通过Binder实体找到用户空间的Server对象,Server收到Binder驱动通知后,Server 进程通过回调Binder对象onTransact()进行数据解包和调用目标方法,Binder驱动根据代理对象沿原路将结果返回并通知Client进程获取返回结果,唤醒Client线程,接收结果。

AIDL  Android Interface Definition Language(Android接口定义语言)

AIDL是基于Binder的,作用是实现进程间的通信。如果需要操作非基础类型的数据,需要序列化。

首先是定义一个Person类继承Parcelable。

package com.example.mylibraryimport android.os.Parcel
import android.os.Parcelableclass Person():Parcelable {var name:String = ""var age:Int = 0constructor(parcel: Parcel):this(){name = parcel.readString().toString()age = parcel.readInt()}constructor(name: String,age: Int):this(){this.name = namethis.age = age}override fun writeToParcel(dest: Parcel?, flags: Int) {dest?.writeString(name)dest?.writeInt(age)}fun readFromParcel(parcel: Parcel): Person? {name = parcel.readString().toString()age = parcel.readInt()return this}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)}}
}

要能操作Person类还需要定义一个AIDL文件

// Person.aidl
package com.example.mylibrary;// Declare any non-default types here with import statementsparcelable Person;

接下来创建自己的AIDL文件,然后声明自己需要的方法。

// IMyAidlInterface.aidl
package com.example.mylibrary;
import com.example.mylibrary.Person;// Declare any non-default types here with import statementsinterface IMyAidlInterface {/*** Demonstrates some basic types that you can use as parameters* and return values in AIDL.*/List<Person> getPeople();void addPerson(in Person person);Person updatePerson(inout Person person);Person updatePerson2(inout Person person);
}

关于参数前的in、out和inout,跨进程时,in参数会把参数的内容传给aidl,但其改动不会同步到调用进程;out参数不会把参数的属性传给aidl(aidl获取的参数对象属性为空),但其改动会同步到调用进程;inout参数则是in和out的综合。不跨进程时,三者则是摆设。

上面一定要导入Person类的正确地址,不然aidl生成对应的java找不到。同步一下。

3、有了接口文件,我们需要定义一个服务类,实现接口方法,在onBind返回实例。

package com.example.mylibraryimport android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log
import kotlin.random.Randomclass PersonService: Service() {var personList:ArrayList<Person> = ArrayList()var random = Random(1)init {val p  = Person("AAAA",20)personList.add(p)}private var personManager = object : IMyAidlInterface.Stub(){override fun addPerson(person: Person?) {val isNull = person == null // 参数为inLog.e("aaa","in person is null--$isNull")if (person != null) {personList.add(person)}}override fun updatePerson(person: Person): Person {personList.set(0,person)return person}override fun getPeople(): MutableList<Person> {return personList}override fun updatePerson2(person: Person): Person {val p1 = Person()p1.age = random.nextInt() % 40p1.name = "mike"personList[1] = p1return p1}}override fun onBind(intent: Intent?): IBinder? {Log.e("bbbbb","有连接请求");Log.e("cccc",intent.toString());return personManager;}
}

4、在manifest中声明

        <service android:name="com.example.mylibrary.PersonService"android:exported="false"android:process=":remote"><intent-filter><action android:name="com.example.text" /><category android:name="android.intent.category.DEFAULT"/></intent-filter></service>

android:exported 该属性用来标示,其它应用的组件是否可以唤醒service或者和这个service进行交互:true可以,false不可以。如果为false,只有同一个应用的组件或者有着同样user ID的应用可以启动这个service或者绑定这个service。

android:process=":remote"  让服务在指定进程名中启动,这里选择”remote”这个名字是随意主观的,你能用其他名字来让这个服务在另外的进程中运行。冒号’:’这个前缀将把这个名字附加到你的包所运行的标准进程名字的后面作为新的进程名称。

现在的结构是这样的。

5、在主app中使用这个服务

前提:app已经依赖了mylibrary这个module

package com.example.textimport android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.example.mylibrary.IMyAidlInterface
import com.example.mylibrary.Person
import com.example.mylibrary.PersonServiceclass MainActivity : AppCompatActivity() {private var isConnected = falselateinit var peopleManager:IMyAidlInterfaceprivate val p = Person("Dustin", 27)override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_main)}private val connection: ServiceConnection = object : ServiceConnection {override fun onServiceConnected(name: ComponentName, service: IBinder) {peopleManager = IMyAidlInterface.Stub.asInterface(service) // 此处的service,就是Service的onBind()方法返回的Stub,必须经过这个方法才能还原成Stub类对象isConnected = trueshow()logger("before add")logger(p.name+"aaaaaaaaaaaaaaaaa")logger("=================")peopleManager.addPerson(p)show()logger("=================")peopleManager.updatePerson(p)logger(p.name+"aaaaaaaaaaaaaaaaa")show()logger("=================")                        peopleManager.updatePerson2(p)show()}override fun onServiceDisconnected(name: ComponentName) {logger(name.toString() + "已经断开连接")isConnected = false}}fun show(){for (i:Int in 0 until peopleManager.people.size){logger(peopleManager.people[i].name)logger(peopleManager.people[i].age.toString())}}private fun logger(info: String) {Log.e("FragmentActivity.TAG", info)}override fun onStop() {super.onStop()tryDisconnectService()}override fun onStart() {super.onStart()tryConnectService()}private fun tryConnectService() {logger("try to connect service")if (!isConnected) {val intent = Intent(this, PersonService::class.java)bindService(intent, connection, Context.BIND_AUTO_CREATE)}}private fun tryDisconnectService() {logger("try to disconnect service")if (isConnected) {unbindService(connection)isConnected = false}}}

结果:

服务进程:

app进程:

我们还没测试过out,我们把AIDL文件的

    Person updatePerson(inout Person person);
改为Person updatePerson(out Person person);

同步,再运行。

看到updatePerson(out Person person);改变不了服务类的数据了。

但是传到服务进程的值并不为空。

分析一下  mAidlInterface = IMyAidlInterface.Stub.asInterface(service)

public static IMyAidlInterface asInterface(IBinder obj){if ((obj==null)) {return null;}IInterface iin = obj.queryLocalInterface(DESCRIPTOR);if (((iin!=null)&&(iin instanceof IMyAidlInterface))) {return ((IMyAidlInterface)iin);}return new Stub.Proxy(obj);
}
private static class Proxy implements IMyAidlInterface{private IBinder mRemote;Proxy(IBinder remote){mRemote = remote;}@Override public IBinder asBinder(){return mRemote;}public String getInterfaceDescriptor(){return DESCRIPTOR;}@Override public void myMethod() throws RemoteException{android.os.Parcel _data = android.os.Parcel.obtain();android.os.Parcel _reply = android.os.Parcel.obtain();try {_data.writeInterfaceToken(DESCRIPTOR);mRemote.transact(Stub.TRANSACTION_myMethod, _data, _reply, 0);_reply.readException();}finally {_reply.recycle();_data.recycle();}}
}

Stub.Proxy同样实现了我们定义的功能接口,而且包含一个BinderProxy对象,当我们在Client进程中调用我们所定义的功能方法时,其实就是调用Stub.Proxy中实现的方法。 在实现该功能方法时,它首先将参数序列化,然后调用BinderProxy的transact()方法,调用该方法以后,Binder驱动会唤醒Server进程中的本地Binder对象, 并调用它的onTransact()方法。

    @Override public boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException{switch (code){case INTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_myMethod:{data.enforceInterface(DESCRIPTOR);this.myMethod();reply.writeNoException();return true;}}    return super.onTransact(code, data, reply, flags);}

TRANSACTION_myMethod,是一个整型,也就是说在Binder中对每一个方法都进行了编号,在transact()方法中传入编号,然后在onTransact()方法中,根据请求的变化调用相应的方法。这里我们看到data接收参数,然后调用本地Binder中定义的功能方法,这里是抽象方法,留有子类实现,最后将结果写入到_reply中,由Binder驱动负责将返回值传递到BinderProxy的transact()方法中的_reply。

Service接口方法调用流程小结

  1. 客户端调用bindService绑定服务时,将触发Service的onBind监听方法。该方法将调用asBinder方法,返回一个Binder对象。
  2. 客户端将通过onServiceConnected回调函数,获取到该Binder对象(以传参的形式传入)。
  3. 客户端获取到Binder对象后,可调用stub.asInterface方法,将其转换为service实现类的对象。
  4. 在asInterface方法中,将判断service与当前进程,是否在同一进程中。若是,则返回stub本身,否则返回stub.proxy。返回结果将作为Service实现类的实例。
  5. 在通过Service实现类的实例调用接口方法时,若为同一进程,则直接调用方法本身。若为跨进程,则调用stub.proxy的对应接口方法,通过Transact方法将信息传送到服务端。此时,客户端将挂起,等待结果返回。
  6. 服务端接收到信息,通过onTransact()方法,根据方法的唯一标识,将信息转发至各对应方法。
  7. 信息处理完成后,再由服务端onTransact返回结果。

文章推荐:

https://blog.csdn.net/weixin_44339238/article/details/110942282

Android—Binder+AIDL相关推荐

  1. aidl使用_借助 AIDL 理解 Android Binder 机制——Binder 来龙去脉

    AIDL 是 Android Interface Definition Language(Android 接口定义语言)的缩写,它是 Android 进程间通信的接口语言.由于 Android 系统的 ...

  2. 由浅入深 学习 Android Binder(一)- AIDL

    Android Binder系列文章: 由浅入深 学习 Android Binder(一)- AIDL 由浅入深 学习 Android Binder(二)- bindService流程 由浅入深 学习 ...

  3. Android Binder机制浅析及AIDL的使用

    参考 轻松理解 Android Binder,只需要读这一篇 图文详解 Android Binder跨进程通信的原理 Android中的Parcel是什么 Android Binder IPC通信机制 ...

  4. 不得不说的Android Binder机制与AIDL

    说起Android的进程间通信,想必大家都会不约而同的想起Android中的Binder机制.而提起Binder,想必也有不少同学会想起初学Android时被Binder和AIDL支配的恐惧感.但是作 ...

  5. Android Binder 学习笔记

    前言: Binder是Android给我们提供的一种跨进程通信方式.理解Binder能帮助我们更好的理解Android的系统设计,比如说四大组件,AMS,WMS等系统服务的底层通信机制就都是基于Bin ...

  6. Android Binder的使用

     Binder是一个深入的话题,由于Binder太过于复杂,所以本文不涉及底层细节,要想要知道底层细节可以去阅读Android Bander设计与实现 - 设计篇.写给 Android 应用工程师的 ...

  7. 理解Android Binder机制(3/3):Java层

    本文是Android Binder机制解析的第三篇,也是最后一篇文章.本文会讲解Binder Framework Java部分的逻辑. Binder机制分析的前面两篇文章,请移步这里: 理解Andro ...

  8. Android Binder 分析——原理

    分析之前说一下原理.为要 android 要搞这么复杂的一个东西.那是因为 android 是个多进程的系统,进程间的数据交换.相互调用(某几个程序配合完成某些业务)就涉及跨进程通信.2个进程不能直接 ...

  9. Android之AIDL使用详解

    1.什么是aidl:aidl是 Android Interface definition language的缩写,一看就明白,它是一种android内部进程通信接口的描述语言,通过它我们可以定义进程间 ...

最新文章

  1. 重磅公开课推荐 | 如何搭建聊天机器人:技术架构剖析
  2. html点击按钮计算两个输入框的和_小程序计算报价功能介绍
  3. 关于Chrome内核88版本无法正常使用Adobe Flash Player公告
  4. Docx模板引擎示例
  5. json decode php 二维,在json_decode /多维数组之后访问JSON数组
  6. android adb驱动win7,win7 64位adb驱动怎么安装_win7安装abd驱动的方法图文步骤
  7. px4原生源码学习-(4)--Nuttx 实时操作系统编程
  8. .nh文件转化为pdf,word
  9. 《名字竞技场 V3.0》 组队功能开放!
  10. PEAP-MSCHAPV2
  11. Bose SoundLink Revolve或者Bose SoundLink Revolve+ AUX音频播放一会没有声音
  12. 介绍了用Meta标签代码让360双核浏览器默认极速模式(google)打开网站不是兼容模式
  13. 移动端手机网页适配iPad与折叠屏设备
  14. SQL server中时间获取少了两天解决方法
  15. (FAQ)VM log是做什么的,4 Way VM又是什么
  16. 新手程序员如何提升自己的编程能力
  17. abp框架学习笔记(三)--Angular和前端
  18. Directory /opt/journalnode/lagou is in an inconsistent state: Can‘t format the storage directo
  19. 高精度运算模板(高精度加减乘除)
  20. 用ps将一张图设置成自己想要的尺寸而不变形

热门文章

  1. centos 6.5 openldap php,centos6.5安装openldap+phpldapadmin
  2. 多重背包单调队列优化思路_动态规划入门——多重背包与单调优化
  3. hibernate版本_Myeclipse 2020.5 版本首发!支持 Java14
  4. quartz获取开始结束时间_王者荣耀s21什么时候开始是9月24日吗?王者荣耀s20赛季结束时间...
  5. c语言中 文件的字符串输入函数是6,【C语言】文件操作及输入输出格式(文件常用的库函数)...
  6. c语言提取图片yiq分量,图片数字水印C语言代码
  7. php声明一个类的关键字,php中怎么实例化一个类
  8. vue-cli搭建的项目打包之后报“资源路径错误资源文件找不到“
  9. python_数据库连接之mysql
  10. WUSTOJ 1282: Start(Java)