android机制系列之五 再探Binder机制(Java C++对应融会贯通)
系列目录 https://blog.csdn.net/jzlhll123/article/category/7671581
上一篇文章https://blog.csdn.net/jzlhll123/article/details/80516277 写的不够成熟,很多东西还不够深入。C++也遗留下来了。今日再探。断断续续也花了1-2周业余时间。
LINUX IPC机制
管道,消息队列,共享内存,信号量,Socket
Binder内存拷贝一次,性能好;安全性好check UID/PID。
Java Binder
接上篇文章android机制系列之二 Binder机制, 如果不了解bindService机制和AIDL,请先展开一些基础学习。
我们实现AIDL或者Binder的时候,几个关键点:
-
IMyRemote implements IInterface
添加自定义func()
-
Stub
MyStub extends Binder(implments IBinder) implements IMyRemote(IInterface)
2.1
实现本地服务端func()
, 实例对象写具体真实方法;2.2
asInterface()
给client用来得到实例对象或者代理对象;2.3
onTransact()
从client传递到binder驱动然后回调给server端的代码,通过parcel封装调用本地代码写回结果; -
Proxy
MyProxy implment IMyRemote(IInterface)
3.1
onServiceConnected
返回的是什么?见
附录1
。我们拿到的是BinderProxy。3.2 onServiceConnected()回到的IBinder,如果同进程queryLocalInterface不为null,就是本地实例;不然,封装成Proxy来使用;即2.2的操作。
3.3
实现客户端func()
,主要通过parcel封装通过transact()
(BinderProxy JNI的方法)传递给binder驱动。 -
Binder
Stub的父类
4.1
attachInterface()
初始化保存mOwner和mDescriptor,用于Stub初始化;4.2
queryLocalInterface()
2.2 asInterface()的返回值;4.3
onTransact()
Stub实现后,再super调一下;4.4
transact()
本地调用则是内部直接调用3.3 onTransact(), 远程客户端是不会使用的 -
BinderProxy implements IBinder
这个是真正远程客户端拿到的IBinder对象。
5.1
transact()
对应transactNative() JNI用来3.2;5.2
queryLocalInterface()
只能return null,因为是远程客户端调用; -
Parcel
parcel在这里面主要2个参数传递,一个传递跨进程数据的任务如writeString8,readCString;
另外一种,通过JNI writeStrongBinder 和 readStrongBinder()解析获取BinderProxy对象。
C++ Binder
Bp指的是Proxy端,对应的是Client端,调用方;Bn指的是Native端,服务实现的自身。
类图:
很多的父类基类管不过来,关注关键的几个类和关键对应java层方法。
-
IMyRemote : IInterface
添加func()
相当于java层的IMyRemote 接口,而且也实现了IInterface;
-
ProcessState&IPCThreadState :
2.1 ProcessState
代表使用Binder的进程,进程单例;
通过IServiceManager::addService在ServiceManager中进行服务的注册
通过ProcessState::startThreadPool启动线程池
通过IPCThreadState::joinThreadPool将主线程加入的Binder中。
2.2 IPCThreadState
代表了使用Binder的线程,这个类中封装了与Binder驱动通信的逻辑,线程单例;
BpBinder::transact其实就是调用的这里transact()
executeCommand回调给onTransact()
-
BnMyRemote : BnInterface(BBinder), IMyRemote
也有的代码直接继承BBinder
onTransact()
从client过来的指令,这边调用真实方法func(),并写入reply;相当于java层的Stub的实现。
-
BpMyRemote:BpInterface, IMyRemote
4.1 客户端是如何拿到BpMyRemote的呢?
java层我们知道java binder 第3节中Proxy,是通过bindService等到回调onServiceConnected()的. C++又是如何拿到的?
附录2
。4.2
func()
实现,通过remote()
, 拿到远程Ibinder* mRemote remote()对象,则通过parcel传递参数和返回值参数transact()到binder驱动。相当于java层的Proxy。
-
BpBinder implements Ibinder
参考附录1的需要牢记的东西。类似java层BinderProxy是通过parcel返回得到的。
参考附录2我们知道它就是客户端使用的对象。
内部通过transact()调用Binder驱动再传达到service端。
-
Parcel
类似java不多解释。只是注意java层主要是java parcel和JNI。而这里的是frameworks/native/libs/binder/Parcel.cpp
总结
java & C++ Binder 对应
作用 | java | C++ | 额外信息 |
---|---|---|---|
MyRemote接口必须继承此项 | IIterface | IIterface | C++提供asInterface用于转换,java靠stub自行编写 |
接口 | IXXX | IXXX | |
XXStub/BnXX必须继承此项 | Binder | BnInterface / BBinder / JavaBBinder | 类似 |
真实本地服务代码实现接口的func并实现onTransact | XXStub | BnXX | 关注它的onTransact()从驱动层回调 |
客户端拿到的远程对象 | BinderProxy | BpBinder | 附录1,附录2 关键字parcel readStrong,onServiceConnected,interface_cast asInterface |
客户端使用的时候封装的对象 | XXProxy | BpXXX | |
MyProxy同时继承MyRemote和此项 | 无 | BpInterface (BpRefBase) | java层没有设计BpInterface这层了但是c++的remote()其实类似java Proxy的mRemote都是拿到的BinderProxy/BpBinder |
封装参数(包含Ibinder对象)传递 | Parcel | Parcel | |
IPC | 无 | ProcessState / IPCThreadState | 封装线程操作transact onTransact动作 |
引用:对应图片几张
源码阅读心得
这里我们要牢记,查看源码的一些心得了!
- 看到某个func() 内部有mRemote/remote()就是客户端拿到BinderProxy/BpBinder;
- func() 都需要将descriptor写入Parcel JNI。如果是获取Ibinder则需要额外写入Binder token, 然后mRemote.transact()/remote->transact()就是调用binder驱动;
- 看到onTransact()就代表已经在server端,onTransact() switch case会从binder驱动的返回结果中parcel解析出一般数据,如果是获取IBinder就是BinderProxy/BpBinder对象;这中间就会调用真实的func(),最后写回去;
- 一般某个文件中会同时有func()2个同名的,要注意区分他的类作用域,一个是Stub/BnXX实体操作;一个是Proxy/BpXX代理操作。
https://blog.csdn.net/jzlhll123/article/details/80865999
回调机制会加深此处的理解。
至此,结束。
附录
附录1 onServiceConnected()拿到的IBinder是什么
bindService流程搜索onServiceConnected相关流程
client->ContextWrapper bindService
—跨进程让AMS去干活–>
ActivityManagerService bindServiceLocked -> ActiveServices requestServiceBindingLocked
—(跨进程binder调用过来的,这里其实是我们的service了)—> ApplicationThreadNative scheduleBindService->
//Proxy
public final void scheduleBindService(IBinder token, Intent intent, boolean rebind,
int processState) throws RemoteException {
Parcel data = Parcel.obtain();
data.writeInterfaceToken(IApplicationThread.descriptor); ///**
data.writeStrongBinder(token); //***
intent.writeToParcel(data, 0);
data.writeInt(rebind ? 1 : 0);
data.writeInt(processState);
mRemote.transact(SCHEDULE_BIND_SERVICE_TRANSACTION, data, null,
IBinder.FLAG_ONEWAY); //***
data.recycle();
}
先写入
//onTransact() //Stub
case SCHEDULE_BIND_SERVICE_TRANSACTION: {
data.enforceInterface(IApplicationThread.descriptor);
IBinder token = data.readStrongBinder(); //***
Intent intent = Intent.CREATOR.createFromParcel(data);
boolean rebind = data.readInt() != 0;
int processState = data.readInt();
scheduleBindService(token, intent, rebind, processState);
return true;
}
可以继续翻阅相关code,原作者这里讲述不是很详细,readStrongBinder是拿到我们服务的descriptor走的Parcel的JNI android_os_Parcel_readStrongBinder , 然后,javaObjectForIBinder 就是构造一个gBinderProxyOffsets class,就是clazz = env->FindClass(kBinderProxyPathName); kBinderProxyPathName = “android/os/BinderProxy” .
->
IBinder binder = s.onBind(data.intent);
—>AMS publishService publishServiceLocked
—>CLIENT 再2次跨进程回去
可以看到,ActivityManagerService最后(1)处调用service的onBind()方法获取service的Stub对象.Stub是binder通信的本地端
.
所以拿到的是BinderProxy对象。
附录2 C++客户端如何拿到BpMyRemote的?
由于所有的服务都addService serviceManager中,并写入descriptor和name,并写入IBinder。
客户端首先要从sp bs = defaultServiceManager()->getService(serviceName);
checkService , 类似java 使用JNI Parcel,而这里frameworks/native/libs/binder/Parcel.cpp writeStrongBinder flatten_binder 拿到
IBinder *local = binder->localBinder();
if (!local) {
BpBinder *proxy = binder->remoteBinder();
if (proxy == NULL) {
ALOGE("null proxy");
}
const int32_t handle = proxy ? proxy->handle() : 0;
obj.type = BINDER_TYPE_HANDLE;
obj.binder = 0; /* Don't pass uninitialized stack data to a remote process */
obj.handle = handle;
obj.cookie = 0;
} else {
obj.type = BINDER_TYPE_BINDER;
obj.binder = reinterpret_cast<uintptr_t>(local->getWeakRefs());
obj.cookie = reinterpret_cast<uintptr_t>(local);
}
//readStrongBinder()
const flat_binder_object* flat = in.readObject(false);
if (flat) {
switch (flat->type) {
case BINDER_TYPE_BINDER:
*out = reinterpret_cast<IBinder*>(flat->cookie);
return finish_unflatten_binder(NULL, *flat, in);
case BINDER_TYPE_HANDLE:
*out = proc->getStrongProxyForHandle(flat->handle);
return finish_unflatten_binder(
static_cast<BpBinder*>(out->get()), *flat, in);
}
}
取出了BpBinder IBinder*。
sp<IServiceManager> sm = defaultServiceManager();
//这里取得就是BpBinder
sp<IBinder> binder = sm->getService(String16("media.camera"));
mCameraService = interface_cast<ICameraService>(binder);
接下来分析下,interface_cast 《T》,就是INTERFACE::asInterface(obj);
android::sp<I##INTERFACE> I##INTERFACE::asInterface( \
const android::sp<android::IBinder>& obj) \
{ \
android::sp<I##INTERFACE> intr; \
if (obj != NULL) { \
intr = static_cast<I##INTERFACE*>( \
obj->queryLocalInterface( \
I##INTERFACE::descriptor).get()); \
if (intr == NULL) { \
intr = new Bp##INTERFACE(obj); \
} \
} \
return intr; \
}
采用了模板代码,也是类似java层,queryLocalInterface()是否得到NULL,为NULL,创建一个BpMyRemote出去。所以,这里的remote()
拿到的是BpXXX。
这里跟java层类似
java是因为service,client,activityManagerService,肯定service和ams是不同进程的。即使service client是同一个进程,附录1我们知道它肯定必须是跨进程的了所以拿到的是BinderProxy。
然后client拿到以后自己去转换本地对象或者封装Proxy使用。
而C++也是Parcel里面拿到的Ibinder* / sp,然后interface_cast(类似我们aidl文件中的asInterface,而且它的内部实现就叫做asInterface)转成BpBinder或者本地实例。
附录3 基类:RefBase,所有类从此派生; 使用sp,wp引用它的子类*
理解下来,设计RefBase与weakref_impl的相互聚合设计了sp;wp则又多一个weakref_type来当做RefBase引用
1. A:RefBase
RefBase:RefBase()
mRefs(new weakref_impl(this))
因为设计模式是,weakref_impl跟RefBase是相互有彼此的。因此会传入自己给mRefs
weakref_imp
mStrong, mWeak 强弱引用计数
析构函数:
1. 没有sp,直接移除mRefs;
2. wp并为0,删除mRefs;
2. sp spA(new A);
sp 构造函数:
A对象指针赋值给m_ptr **直接调用A的incStrong
1. 增加弱引用计数;
2. 增加强引用计数;
3. 第一次onFirstRef
sp析构:
decStrong
1. 减少强引用计数;
2. 是否最后一个,是则释放A对象;
3. 减少弱引用(decWeak)
3.1 减少,发现还有wp,return
3.2 根据是否存在sp,否,删除A;是,删除mRefs
3.3 如果wp,删除A。
wp wpA(new A);
wp 构造函数
A对象指针赋值给m_ptr 比sp多一个m_refs(接口),m_refs是由A createWeak而来
1. 增加弱引用计数;
2. wp的m_Refs得到赋值
wp析构:
就是decWeak上面减少弱引用的操作3.1~3.3
使用层面来讲就简单了。
1. 类A继承RefBase
2. 使用的时候,A* a = new A(); sp<A> spA = a; sp<A> wpA = a; sp<A> spC = new A();
3. 任何一个类,都可以实现它。当他不存在。
2018.07.16补充:
wp提升为sp,就是使用promote()得到强指针。类似java层的weakRefercence,get()以后就是一个实体类对象了。
鸣谢:
- Android Binder设计与实现 – 设计篇 https://blog.csdn.net/freshui/article/details/54926111
太提纲挈领,暂时不能深入理解。只提到他对linux IPC的认识。
- 理解 Android Binder 机制(二):C++层 https://www.2cto.com/kf/201704/635248.html
本文大部分参考它,结合手中的android6.0 5.1源码学习。
- Android深入浅出之Binder机制 http://www.cnblogs.com/innost/archive/2011/01/09/1931456.html
此文通俗,以MediaService为基础学习。少量部分逻辑理不顺结合第二个帖子,和源码分析。
- Binder通信——用户空间C/C++层架构与Java层对接点概述 https://blog.csdn.net/a372048518/article/details/75020240
简简单单几张图。但是在学习完本帖回头来看,很有感悟。
- bindservice 过程 https://blog.csdn.net/xutao20170209/article/details/72677004
结合源码,辅助一些onServiceConnected流程的理解。