Binder:为什么要通过onTransact()调用目标方法

本文探讨了Android中Binder机制的onTransact方法的作用及其重要性。解释了为什么需要通过onTransact调用远程方法,而不是直接指定方法名。文章还深入分析了onTransact如何帮助实现进程间的命令模式调用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载自:http://www.jianshu.com/p/b260051237fe

 

Binder:为什么要通过onTransact()调用目标方法

 

0x00 背景

最近被提出一串问题:为什么android.os.Binder要提供onTransact()方法给子类重写。为什么要通过Client:invokeMethod -> onTransact() -> Service:targetMethod这一曲折过程来调用一个远程方法,为什么不能直接指定方法名称来调用。

这些问题阐述了同一个疑问:对于调用者而言,目标方法为何远在天边。

实际上,这并不是一个无关紧要的问题。尝试解答此问题有助于了解API设计者的初衷,以便从不同角度探究可能潜在的问题。了解系统创作者的思想,从而更好地使用其设计的接口及系统特性,避免出现错误决策导致长期迭代中难以维护。

本文假设你已具备IPC(进程间通信)开发经验。

0x01 AIDL 与 onTransact()

做一些简单回顾:

首选,无论定义于何处,.aidl文件始终是生成目标.java文件的声明标记。大多数情况下,对android.os.IInterfaceandroid.os.Binder的继承及实现不应当手动操作。所有的生成行为应当由.aidl声明来完成。此外,所生成的目标文件位于app/build/generated/source目录下。

其次,android.os.Binder实现于android.os.IBinderandroid.os.Binder实现了大多数进程状态所必要的功能,以及所有必要的功能调度。

.aidl存在的目的就是为了剔除大量重复性的工作。这其中包括,所生成目标文件下的类种类Stub方法:onTransact(int, android.os.Parcel, android.os.Parcel, int)。此方法重写自android.os.Binder。此外,.aidl是为对外暴露接口而设计的。

0x02 onTransact() 干了些什么

Binder.onTransact()是为Binder.transact()的调用而准备的。Binder.transact()做了两件事:

public final boolean transact(int code, Parcel data, Parcel reply,
        int flags) throws RemoteException {
    if (data != null) {
        data.setDataPosition(0);
    }
    boolean r = onTransact(code, data, reply, flags);
    if (reply != null) {
        reply.setDataPosition(0);
    }
    return r;
}

此段代码位于android/os/Binder.java

首先,在调用Binder.onTransact()之前及之后,分别对请求结构的引用及返回结构的引用重置读写position,以及调用Binder.onTransact()。在此提醒,Binder.transact()的调用者是Stub下的内部类Proxy中的各个.aidl中定义的方法。

最终,千辛万苦地,终于来到了.aidl自行生成实现的Binder.onTransact()方法了。特别的是,有两个地方值得去注意:

  • 其一,在最终的开发中,将会继承抽象类Stub,并实现所有在.aidl中定义的方法。这些具体方法的直接调用者,正是当前我们所在的onTransact()方法;

  • 其二,正是基于上面一条,可以得知:无论远程调用者(Client)身处何方,最终,一定会经过此处的onTransact()方法,并由onTransact()直接调用目标方法。

要知道,传入onTransact()方法的参数中,拥有目标方法的ID、指向参数的引用,以及指向返回结果的引用。所有远程调用者(Client)想要做的事,都通过层层调用及参数包装汇聚到onTransact(),再由onTransact()分发到真正的目标方法执行。

那么问题来了。为什么?

0x03 关键问题:进程隔离

一个简洁明了的回答是:除了预先定义的接口,其余的一切实现在进程间均相互不可见。

现在,以更直观的方式来展示调用者(Client)与服务(Service)间的关系:


上下层关系

显然,所有的Client亦或是Service,都是平级的。原因显而易见:这部分运行时程序,都是基于Android Framework开发的。

那么,如果A程序自行定义了接口,B程序怎样知道A程序定义了接口?换言之:B程序该通过什么方式来查找A程序中的自定义接口以至调用?显然,所有基于Android Framework开发的程序,都不存在上下级关系。

同时,由于虚拟机相互独立,因此这些程序并不在同一个运行时中。两两之间相隔一堵不透明的墙,它们唯一可见的,就是下层的Binder元素。

答案就此基本浮出水面。Client与Service的状态是不可预知的,使用Binder Driver隐藏进程间调用细节,并通过Binder.onTransact()分发调用指令,最终在参数引用中写入计算结果——这一过程实现了设计模式中的简易命令模式。整个进程间调用作用于Binder Driver,至于Binder.onTransact()格外引人瞩目,则是因为它是整个过程的末端操作。

正如上图所示,把所有请求汇聚到onTransact(),具体需要请求哪个方法,则抽象为id处理。另外,所有目标方法的请求参数及返回体都要求是基本类型或被.aidl所定义的。这意味着在传输过程中所有信息都被视作“流”来处理。

0x04 更多思考

理解Binder调度过程有助于设计更易于维护的接口——尤其是库。某些需求的实现可能需要对ProxyStub进行手动编辑,此时理解API设计者的意图显得极为重要。

毕竟,维护那些凭直觉写出的代码,简直就是灾难。



作者:Abel_Joo
链接:http://www.jianshu.com/p/b260051237fe
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
asii
2楼 · 2016.07.29 17:24

说到了重点 使用了命令模式 其实aidl生成的java类很值得我们参考 本身是一个代理模式的实现  而他的子类才是实现各个操作的真正实现者 这里用到了模板模式  这些底层模块的建设处处蕴含着设计者对DIP设计原则的理解

 

----- pid 15917 at 2025-06-17 16:01:29.796255295+0300 ----- Cmd line: /system/bin/audioserver "binder:15917_4" sysTid=16095 #00 pc 00000000000b7750 /apex/com.android.runtime/lib64/bionic/libc.so (syscall+32) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) #01 pc 00000000000925e0 /apex/com.android.runtime/lib64/bionic/libc.so (__futex_pi_lock_ex(void volatile*, bool, bool, timespec const*)+332) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) #02 pc 00000000000a2944 /apex/com.android.runtime/lib64/bionic/libc.so (PIMutexTimedLock(PIMutex&, bool, timespec const*)+212) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) #03 pc 00000000000b1b60 /system/lib64/libc++.so (std::__1::mutex::lock()+12) (BuildId: 53e0091d25a788802d2d3a5324f79b527df4913f) #04 pc 00000000000e57e8 /system/bin/audioserver (android::audio_utils::mutex_impl<android::audio_utils::AudioMutexAttributes>::lock()+140) (BuildId: a5c5911886560f96215f83fa00076034) #05 pc 000000000021b2f4 /system/bin/audioserver (android::AudioPolicyService::getDevicesForAttributes(android::media::audio::common::AudioAttributes const&, bool, std::__1::vector<android::media::audio::common::AudioDevice, std::__1::allocator<android::media::audio::common::AudioDevice>>*)+260) (BuildId: a5c5911886560f96215f83fa00076034) #06 pc 0000000000039dfc /system/lib64/audiopolicy-aidl-cpp.so (android::media::BnAudioPolicyService::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+6320) (BuildId: 42f83b19a621bcae5536880c786c936d) #07 pc 00000000001f5570 /system/bin/audioserver (android::AudioPolicyService::onTransact(unsigned int, android::Parcel const&, android::Parcel*, unsigned int)+1112) (BuildId: a5c5911886560f96215f83fa00076034) #08 pc 0000000000056524 /system/lib64/libbinder.so (android::IPCThreadState::executeCommand(int)+1148) (BuildId: 217d4a46760a940dfed21538f547262d) #09 pc 0000000000054a08 /system/lib64/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+444) (BuildId: 217d4a46760a940dfed21538f547262d) #10 pc 000000000007ec7c /system/lib64/libbinder.so (android::PoolThread::threadLoop()+92) (BuildId: 217d4a46760a940dfed21538f547262d) #11 pc 000000000000e5cc /system/lib64/libutils.so (android::Thread::_threadLoop(void*)+252) (BuildId: d1d3bf8fe6fecd6921ac714b5ef42e28) #12 pc 00000000000a14e8 /apex/com.android.runtime/lib64/bionic/libc.so (__pthread_start(void*)+196) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) #13 pc 000000000009394c /apex/com.android.runtime/lib64/bionic/libc.so (__start_thread+68) (BuildId: 4545a63f7d7b92b0c45c26fe530b66de) 找到当前线程等待调用返回的目标进程为15917,后面的信息怎么理解
最新发布
07-04
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值