3. AOSP通信机制:Binder、Handler、Service
做AOSP测试框架,绕不开这三兄弟——Binder、Handler、Service。说白了,整个Android系统的“骨架”就是靠它们撑起来的。你要Mock回放,就得先把它们怎么通信、怎么调度、怎么绑定搞清楚。不然你写出来的Mock方案,很容易在真实设备上崩得一塌糊涂。
3.1 Binder:跨进程的“快递员”
Binder是Android里最核心的IPC机制。几乎所有跨进程调用,背后都是Binder在跑。我刚开始接触AOSP时,一直觉得Binder很玄乎——不就是个远程调用嘛?后来踩了坑才明白,它的设计思路和传统Linux IPC完全不同。
- 一次拷贝:传统IPC(如Socket、管道)需要两次数据拷贝(用户态→内核态→用户态),而Binder只需要一次,性能高不少。
- C/S架构:Client发起请求,Service提供服务,Binder驱动负责中转。每个Service都有一个唯一的标识(handle)。
- 引用计数:Binder对象有强引用和弱引用,跨进程传递时不会乱释放。
举个例子,你在测试框架里Mock一个系统服务,实际上就是接管了那个Service的Binder接口。我建议你在做回放之前,先把目标Service的Binder接口走一遍,用ServiceManager.getService()拿到真实的Binder对象,然后序列化它的调用数据。
// 获取真实Binder对象
IBinder binder = ServiceManager.getService("window");
// 解析接口描述符
String descriptor = binder.getInterfaceDescriptor();
// 记录每次transact调用
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
int code = TRANSACTION_ADD_WINDOW;
binder.transact(code, data, reply, 0);
// 把data和reply存起来,后面回放用
3.2 Handler:线程间的“邮递员”
Handler是Android里线程通信的基础。为什么需要它?因为UI线程不允许阻塞,也不允许非UI线程直接更新UI。Handler+Looper+MessageQueue这三件套,构成了一个消息驱动模型。
我记得有一次写自动化测试,要模拟用户点击事件。直接用InputManager.injectInputEvent()发现事件没被处理,折腾了半天——原来是因为事件是在测试线程发的,而目标窗口的Handler绑定的是主线程Looper,消息队列没对上。后来我手动把事件post到主线程Handler里,问题就解决了。
在Mock回放框架里,Handler的回放尤其容易出问题。为什么呢?因为时间敏感。真实场景中,Handler消息有when字段,表示延迟时间。回放时如果忽略这个时间,可能会导致状态不一致。
| 字段 | 含义 | 回放处理方式 |
|---|---|---|
what | 消息ID | 严格匹配 |
obj | 附带对象 | 需要深拷贝或序列化 |
when | 触发时间(毫秒) | 建议按比例压缩或保持相对延迟 |
callback | Runnable回调 | 回放时直接执行,不用再创建Looper |
Bundle参数。回放时我直接用了原始引用,结果多个回放线程共享同一个Bundle,数据互相覆盖。后来我统一做了Parcel.obtain()反序列化,每个回放线程独立一份数据。3.3 Service:系统能力提供者
Service在Android里分两种:系统服务(System Service)和应用服务。我们搞测试框架,主要打交道的是系统服务——比如ActivityManagerService、WindowManagerService、PackageManagerService等等。
每个系统服务都运行在system_server进程中,通过Binder对外暴露接口。在AOSP源码里,你可以在frameworks/base/services/core/java/com/android/server/下面找到它们。
你想Mock一个系统服务,传统做法是用Mockito或者EasyMock打桩。但对于AOSP这种大型系统,Mockito搞不定——因为有些服务内部引用了大量Android Framework的内部类,静态方法、final类比比皆是。
所以我们的框架走的是另一个路子:录制-回放。先让真实设备跑一遍测试用例,把所有Binder调用、Handler消息都记录下来,然后在Mock环境里原样回放。
transact()参数,还要记录当前线程、Looper状态、系统时间戳。回放时,需要把这些上下文全部还原,否则Mock出来的行为跟真实情况对不上。3.4 三者协作:一张图看懂
下面这张图,画出了Binder、Handler、Service在AOSP测试框架里的协作关系。我画了很多遍才定稿——每次改版都会发现新的依赖关系。
3.5 总结:回放时要关注的三件事
- 线程归属:Binder调用是在Binder线程池,Handler消息是在目标Looper线程,Service运行在system_server。回放时不能混用线程。
- 时间顺序:Handler消息的
when字段和Binder调用的时间戳,决定了事件发生的先后顺序。乱序回放会导致逻辑错误。 - 状态依赖:很多Service有内部状态(比如Activity栈、窗口Z-order),回放前需要先初始化到录制时的初始状态。