Android AIDL理解

  • 业务逻辑语境中,实现Service onBInd的那个app进程是服务端。技术上,A进程调用B进程的方法,B就是服务端;B调用A传递过来的callback方法,A就是服务端。

  • aidl接口参数的in out修饰符看的是技术上的服务端,数据流进就是in,数据流出就是out。实际上有更简单明确的理解,对于aidl接口的调用方,看到in参数就想:我把参数in进去就不用管了,对于out参数就想:我对这个参数我待会还得out出来用,有out参数的方法是同步阻塞的。inout具备双重特性

  • 服务端拿到in参数的副本进行访问和修改,不会影响到客户端的实参。客户端调用方法时传入out参数,是不是实例对象无所谓,声明变量名 user = null,调用xxx(user),由于是out参数没有被binder驱动序列化到服务端,服务端拿不到也不关心user里在客户端那边的实例细节(服务端拿到的参数是binder驱动给的初始值没有意义),服务端创建一个user对象,处理好数据,赋值给user变量名,客户端那边的user变量就引用服务端实际处理的user对象的副本。客户端调用xxx(user),得等服务端xxx方法执行完处理完user数据,out参数aidl方法是一个同步阻塞的方法,不能用oneway修饰

  • aidl接口原生支持参数类型是基本数据类型和String类型,默认都是in。自定义参数类型分为两种一种是bean,得声明android.os.Parcelable接口,支持binder机制序列化/反序列化,一种是回调对象/监听器,也得声明为aidl interface,自定义参数类型最好显示声明in out

  • oneway修饰的方法,是异步的,不能有返回值,只能有in参数,调用处不用等待服务端方法实现处的执行结束。不加oneway默认是同步阻塞的,就像普通的方法调用一样,无论有无返回值,调用处都得等这个远程方法执行完,服务端方法执行完,才走调用处后续代码。

  • aidl接口实现跨进程通信是靠Android的binder机制,跨进程调用时要经过内核的binder驱动。服务端进程在内核binder驱动中映射一块缓存(binder内存映射技术),服务端可以访问。客户端通过binder驱动copy一次数据到缓存区,服务端就可以访问。

  • 如果aidl接口的调用方和实现方在一个进程内,就不用走内核的binder驱动。

  • binder机制是Android系统中跨进程通信的核心,Broadcast、ContentProvider等都是依赖binder机制。aidl接口也是。

  • 服务端定义aidl接口文件,Do.aidl,会被编译为一个java类,里面有接口存根Stub,里面定义abc三个方法,由上到下顺序,在Service onBind中实现Do.stub中的abc方法并返回binder对象。

  • 客户端bindService异步请求绑定服务端service,连接回调走的是ServiceConnection中的方法。在什么线程调用bindService,客户端收到的连接回调就走在什么线程。

  • bindService时如果service端没有起来,会被拉起。建立连接后,系统会维护这个binder连接,如果服务端crash挂掉,系统会拉起服务端,保证连接的健壮性(一个项目的实践经验,有待确认)

  • 客户端通过ServiceConnection的onServiceConnected方法拿到客户端binder对象的引用,通过Do.Stub.asInterface(binder)转化为程序里面可调abc方法的binder代理对象。

  • 服务端和客户端都得有Do.aidl文件,双方都才能访问abc方法。互相复制一份相同的aidl文件,或者一方打包sdk给另一方集成

  • 注意aidl的版本管理:aidl文件要具有"包名.类名"唯一性,Binder驱动会对双方的aidl文件里面定义的方法abc,按照顺序进行编号,编号->映射服务端对方法的实现。客户端调用方法时,传递方法编号,附带数值/可序列化的数据包作为参数,到Binder驱动中,binder驱动进行编号方法名映射,序列化/反序列化处理。

  • 双方的aidl文件是相同的,都是包名.类名、方法定义、顺序都是一样的,举例编号1->a,2->b,3->c。最好双方aidl定义都一样,保持版本同步。如果服务端的aidl定义供很多客户端使用,下次改版时如果只是针对某个app aClient,不想大面积修改发版,增加一个方法d,得追加到最后,aidl文件同步给这个aClient,这个aClient和服务端都有编号1->a,2->b,3->c,4->d,其他客户端使用老版本aidl接口仍然是1->a,2->b,3->c,都可以编号方法映射正确。

  • 如果双发都是1->a,2->b,3->c,现在服务端要改b为e,那么服务端1->a,2->e,3->c,客户端没改aidl定义,那么客户端仍然是1->a,2->b,3->c。那么客户端调用b时,告知binder驱动编号2,而在服务端2映射e方法,参数列表也可以能不一致,最终调用错乱,不符合业务逻辑。

  • aidl方法调用按照编号映射方法,而不是方法名匹配。当同一套aidl接口,涉及多端时,一定注意版本控制。注意同步/追加方法,绝对不能单方修改方法顺序

  • Binder驱动为服务端维护一个线程池,方法的调用是在那个线程时客户端的先择,服务端作为方法的实现端,被客户端调用时,方法执行在服务端binder线程中。

相关推荐
老师用之于民21 小时前
【DAY21】Linux软件编程基础&Shell 命令、脚本及系统管理实操
linux·运维·chrome·经验分享·笔记·ubuntu
路由侠内网穿透.21 小时前
本地部署代码托管解决方案 Gitea 并实现外部访问( Windows 版本)
运维·服务器·网络协议·gitea
serve the people21 小时前
python环境搭建 (十三) tenacity重试库
服务器·python·php
jake don1 天前
GPU服务器搭建大模型指南
服务器·人工智能
darkb1rd1 天前
五、PHP类型转换与类型安全
android·安全·php
江畔何人初1 天前
pod的内部结构
linux·运维·云原生·容器·kubernetes
齐鲁大虾1 天前
Linux下用什么编程语言方便开发B/S架构程序
linux·运维·架构
gjxDaniel1 天前
Kotlin编程语言入门与常见问题
android·开发语言·kotlin
csj501 天前
安卓基础之《(22)—高级控件(4)碎片Fragment》
android
范纹杉想快点毕业1 天前
状态机设计与嵌入式系统开发完整指南从面向过程到面向对象,从理论到实践的全面解析
linux·服务器·数据库·c++·算法·mongodb·mfc