Android 跨进程通信-从源码分析AIDL跨进程通信实现
Posted 好人静
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 跨进程通信-从源码分析AIDL跨进程通信实现相关的知识,希望对你有一定的参考价值。
前言
在Android 跨进程通信-(八)AIDL中的代理模式之源码分析 中其实主要还是在总结AIDL中的代理模式的体现,也提到了Client和Server之所以能够进行交互数据,最主要的原因就是依赖Binder驱动,这次就是从Client和Server传递数据的过程在总结下流程,并且就是一一解决之前遇到的一些问题。
简单的拿应用层的AIDL服务来看下这个源码的调用过程,大体分为:
- 1.bindService()将Client和Server建立关系
- (1)Server通过onBind()将Stub返回给Client(Binder驱动在Server是Binder)
- (2)Client在onServiceConnected()中得到BinderProxy(Binder驱动在Client就是BinderProxy)
- 2.Client通过BinderProxy来调用接口方法
- 3.驱动通知Server来调用对应的接口方法
一 Client和Server建立关系
在前面的Android 跨进程通信-(四)Binder机制之Server提到Client进程调用到bindService()来创建Service的时候,会最终调用到AMS中的startProcessLocked()来通知Zygote进程来创建Service进程,当创建Service进程成功并完成初始化之后,就会加载入口类ActivityThread。
1.bindService()
在Activity中通过bindService()来将Client和Server建立关系,会调用到ContextWrapper中的bindService()。Activity、Service直接或间接继承了ContextWrapper。
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
return mBase.bindService(service, conn, flags);
}
这里传入了一个ServiceConnection,用于当Client和Server建立连接的时候,通过onServiceConnected()返回给Client一个IBinder对象,该对象其实是一个BinderProxy,是Server的Binder驱动的本地代理。
遗留问题:ContextWrapper和ContextImpl这里是代理模式还是装饰模式呢?从命名上感觉是装饰者模式。后面需要研究下这个地方。
该bind功能的具体实现就是ContextImpl ,该文件位于/frameworks/base/core/java/android/app/ContextImpl.java,其中部分逻辑代码如下:
@Override
public boolean bindService(Intent service, ServiceConnection conn,
int flags) {
warnIfCallingFromSystemProcess();
return bindServiceCommon(service, conn, flags, mMainThread.getHandler(),
Process.myUserHandle());
}
private boolean bindServiceCommon(Intent service, ServiceConnection conn, int flags, Handler
handler, UserHandle user) {
// Keep this in sync with DevicePolicyManager.bindDeviceAdminServiceAsUser.
IServiceConnection sd;
........
if (mPackageInfo != null) {
//从ServiceConnection获得一个IServiceConnection
sd = mPackageInfo.getServiceDispatcher(conn, getOuterContext(), handler, flags);
}
........
try {
IBinder token = getActivityToken();
//调用AMS中的bindService()来创建出Service进程以及完成绑定
int res = ActivityManager.getService().bindService(
mMainThread.getApplicationThread(), getActivityToken(), service,
service.resolveTypeIfNeeded(getContentResolver()),
sd, flags, getOpPackageName(), user.getIdentifier());
........
}
该方法中首先要将ServiceConnection转换成IServiceConnection。ServiceConnection并不支持跨进程,而IServiceConnection可以跨进程通信。ServiceDispatcher就是负责维护ServiceConnection和IServiceConnection之间的关系。
接下来就是通过AMS的bindService来完成这个绑定过程。这里在Android 跨进程通信-(四)Binder机制之Server中已经分析过了,这个时候AMS会通知Zygote进程来fork出该Service进程,然后加载ActivityThread类,通过消息处理机制先后处理CREATE_SERVICE、BIND_SERVICE对应的事件。
2.ActivityThread中的BIND_SERVICE事件
在ActivityThread中处理BIND_SERVICE事件的时候,会完成两个事情:
private void handleBindService(BindServiceData data) {
Service s = mServices.get(data.token);
.........
if (!data.rebind) {
IBinder binder = s.onBind(data.intent);
ActivityManager.getService().publishService(
data.token, data.intent, binder);
- 第一个:就是调用到service中的onBind()
在应用层的Serivice中,我们会将Stub的继承类的实例通过onBind()返回给Client。在new一个Stub的实例的时候,其中Stub的构造函数有一些关键代码:
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
最终在Server进程中调用到Binder的attachInterface(),那么该Stub的继承类保存到Server进程中,当Client通过onBind()返回的IBinder对象queryLocalInterface()的时候,就会得到该Stub的继承类的实例。
- 第二个:调用到AMS的publishService()回调到ServiceConnection的onServiceConnected()
在BIND_SERVICE事件最终会调用到/frameworks/base/services/core/java/com/android/server/am/ActiveServices.java的publishServiceLocked(),代码如下:
void publishServiceLocked(ServiceRecord r, Intent intent, IBinder service) {
........
try {
c.conn.connected(r.name, service, false);
}
........
}
此时通过IServiceConnection来调用到Client的onServiceConnected()。
遗留问题:这里理解的不是很透彻,后面在细细研究下!
二 Client调用接口方法
1.应用层调用逻辑
Client在调用接口方法时,例如
public void onServiceConnected(ComponentName name, IBinder service) {
baiduPushMessageService = IBaiduPushMessageService.Stub.asInterface(service);
baiduPushMessageService.setBaiduMessageTitle("标题");
......
}
2.aidl编译的java文件调用逻辑
asInterface()在跨进程通信baiduPushMessageService对应的就是Stub.Proxy的实例,所以最终会调用到Stub.Proxy的setBaiduMessageTitle(),如
@Override public void setBaiduMessageTitle(java.lang.String msg) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(msg);
boolean _status = mRemote.transact(Stub.TRANSACTION_setBaiduMessageTitle, _data, _reply, 0);
.........
}
而我们传入的mRemote即为BinderProxy。所以这里Client通过BinderProxy.transact()将序列化的数据打包发给向Binder驱动,其中transact()的对应字段如下:
/**
*
* @param code:要执行动作的标示
* @param data:从Client传到Server序列化的数据,不能为空
* @param reply:从服务器返回的序列化的数据,可能为空
* @param flags:0表示阻塞等待该方法调用结束;1表示执行该方法后立即执行
* @return
*/
public boolean transact(int code, Parcel data, Parcel reply, int flags);
3.Framework层BinderProxy的逻辑
由于IBaiduPushMessageService.Stub.Proxy子类中没有复写transact(),所以调用到Binder.Proxy的transact(),如下:
public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
.......
try {
return transactNative(code, data, reply, flags);
}
.......
}
然后通过JNI调用到C++的具体实现代码/frameworks/base/core/jni/android_util_Binder.cpp的android_os_BinderProxy_transact(),如下:
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags) // throws RemoteException
{
.........
//将java层的BinderProxy转换成Native的BpBinder
IBinder* target = (IBinder*)
env->GetLongField(obj, gBinderProxyOffsets.mObject);
.........
//调用到BpBinder的transact
status_t err = target->transact(code, *data, reply, flags);
.........
}
在这方法中就是将BinderProxy转换成Native的BpBinder,然后调用BpBinder::transact,这样调用逻辑就到了Native的BpBinder。
4.Native层BpBinder的逻辑
通过系统调用,直接调用到Native层封装的BpBinder::transact,该文件位于/frameworks/native/libs/binder/BpBinder.cpp,是对Binder驱动在Native的封装。
status_t BpBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
// Once a binder has died, it will never come back to life.
if (mAlive) {
status_t status = IPCThreadState::self()->transact(
mHandle, code, data, reply, flags);
if (status == DEAD_OBJECT) mAlive = 0;
return status;
}
return DEAD_OBJECT;
}
然后调用到IPCThreadState,该类具体位于/frameworks/native/libs/binder/IPCThreadState.cpp,而IPCThreadState是Native层具体的和Binder驱动进程交互的操作类,完成具体的进程的IPC数据读写,最终通过IPCThreadState::talkWithDriver()通过ioctl()向Binder驱动发送写命令(ProcessState负责打开Binder驱动,并做好内存映射,该过程发生在创建进程阶段)。这样就完成Client进程将要传递的数据发送到了Binder驱动中。
三 Binder驱动通知Server
1.Native层的BBinder的逻辑
Binder驱动会通知Server进程开始读取Client进程传过来的信息。与Client从应用层传到Kernel的顺序正好相反,Binder驱动从kernel一直传到Server的Native的IPCThreadState::talkWithDriver(),在IPCThreadState::waitForResponse就会接收到该消息,最后调用到IPCThreadState::executeCommand()来继续向下执行,如下:
status_t IPCThreadState::executeCommand(int32_t cmd)
{
......
case BR_TRANSACTION:
{
if (tr.target.ptr) {
if (reinterpret_cast<RefBase::weakref_type*>(
tr.target.ptr)->attemptIncStrong(this)) {
error = reinterpret_cast<BBinder*>(tr.cookie)->transact(tr.code, buffer,
&reply, tr.flags);
reinterpret_cast<BBinder*>(tr.cookie)->decStrong(this);
}
......
然后调用到Binder驱动在Native的封装的BBinder::transact,如下,具体代码位于/frameworks/native/libs/binder/Binder.cpp
status_t BBinder::transact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
......
err = onTransact(code, data, reply, flags);
......
}
其中这里的BBinder有两个子类:一个是JavaBBinder,就是Binder驱动在非kernel层使用Java实现的,如我们在应用层定义的Service;另外一个是BnInterface,即Binder驱动在非kernel层用C来实现的,如MediaPlayService。所以这里就是调用到JavaBBinder::onTransact,具体的代码位于/frameworks/base/core/jni/android_util_Binder.cpp的JavaBBinder。
所以之前在 Android 跨进程通信-(八)AIDL中的代理模式之源码分析 中的4.Binder.allowBlocking()提到的Server进程中的Binder驱动在Native对应的类为BBinder,完善之前的示意图如下:
2.Framework层的Binder的逻辑
在JavaBBinder::onTransact的相关代码如下:
virtual status_t onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags = 0)
{
//这里通过jni调用到在Framework层的相关java方法
//gBinderOffsets.mExecTransact = GetMethodIDOrDie(env, clazz, "execTransact", "(IJJI)Z");
jboolean res = env->CallBooleanMethod(mObject, gBinderOffsets.mExecTransact,
code, reinterpret_cast<jlong>(&data), reinterpret_cast<jlong>(reply), flags);
这里通过虚拟机的CallBooleanMethod反向调用了一个用gBinderOffsets.mExecTransact指向Java的方法,其中对应的就是Binder中的execTransact(),这样有Native层就调用到了Framework层的Binder,如下:
// Entry point from android_util_Binder.cpp's onTransact
private boolean execTransact(int code, long dataObj, long replyObj,
int flags) {
.......
res = onTransact(code, data, reply, flags);
.......
在前面我们定义了Stub来继承Binder,那么现在就会调用到AIDL文件编译生成java文件的Stub里面的onTransact()。
3.aidl编译的java文件的逻辑
在Stub的onTransact(),如下:
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
.......
case TRANSACTION_setBaiduMessageTitle:
{
data.enforceInterface(descriptor);
java.lang.String _arg0;
_arg0 = data.readString();
this.setBaiduMessageTitle(_arg0);
reply.writeNoException();
return true;
}
很显然就是调用在子类里面实现的setBaiduMessageTitle()的具体逻辑。
这样经过上面的一系列的调用,就完成了Client和Server进程的RPC。
四 总结
一个从Client到Server的RPC流程如下:
- 1.通过bindService()让Zygote进程fork出Server进程,完成Server进程的初始化功能加载。同时建立Client和Server的关系(遗留问题:这个关系是一种什么关系呢?系统Service在通过ServiceManager注册和管理,在Binder驱动会有一个结构体来知道Client和对应的Server,那现在应用层的Service呢?)
- 2.Client进程持有的是BinderProxy,当要进行RPC的时候,会通过BinderProxy的transact()通过jni(android_utils_Binder)调用到Native层的BpBinder::transact,在BpBinder中会最终调用到IPCThreadState::transact()完成通过ioctl()信息从Client发送到Binder驱动;
- IPCThreadState是Native层负责与Binder驱动进行交互的操作类,完成IPC的数据读写;而ProcessState为每个进程打开Binder设备文件,建立内存映射;
- 3.Binder驱动接收到信息之后,就会找到对应的Server进程,然后从kernel通知到IPCThreadState,在IPCThreadState维护着一个死循环,该循环就会接收到消息,然后调用的BBinder::transact();
- Server进程访问Binder驱动的封装有两种:一种是Binder驱动在非kernel层是用C实现的,那么就对应BnInterface;另外一种是在非kernel层用Java实现的,那么对应的就是JavaBBinder,两个都为BBinder的子类;
- 4.BBinder::transact()调用到子类JavaBBinder的onTransact(),通过jni(JavaBBinder已经为jni调用)调用到Binder的onTransact(),从而完成整个流程。
简答将里面的方法调用如图所示:
以上是关于Android 跨进程通信-从源码分析AIDL跨进程通信实现的主要内容,如果未能解决你的问题,请参考以下文章