Binder进程间通信详解
Posted lmf-techniques
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Binder进程间通信详解相关的知识,希望对你有一定的参考价值。
前言
隔行如隔山,这句话同样适用于任何时候,即时同一个专业,深入下去的话,差别也是巨大的。今天,讲下自己如何学习binder进程间通信的机制的一些见解。开始的时候,只知道 Binder 是个很底层的东西,甚至对于具体是什么用都不是很清楚。
主要是通过两种方式:
看别人写的Binder博文
目的很简单,大概了解Binder是个什么东西,有哪些核心的东西,对于看源码的时候选择性过滤有帮助,最好是看了后画下思维导图总结下或者可以画下流程图。看Binder源码
对于切入点的话,从最熟悉的客户端入手;选择典型的具体例子,分析下前面从他人那边看到的重点。
Binder世界之门
- 学习如何使用东西和思考为什么要创造东西是完全不一样的,很多人写文章往往是忽略了后者。
想下如果两个进程需要相互通信,需要做什么? 假设其中一个进程是客户端进程,另一个是服务端进程,这里约定简称客户端与服务端。
- 1.客户端需要知道哪一个是他要调用的服务端的方法。
- 2.客户端如何传递和接收数据给服务端。
- 3.屏蔽底层通信的细节,包括数据交换通过共享内存。
第一个问题很简单,搞一个唯一的标识符,通过包名+类名。
第二个问题,可以使用实现Parcelable接口的类,why? 这是因为 android 系统可通过它将对象分解成可编组到各进程的原语。
第三个问题,封装一个类来具体的实现,他的名字叫Binder,然后这个binder的话,需要服务端来继承。
具体看源码
找好切入点
- 由于binder是支撑Android系统的重要组成部分,binder从源码来说是很庞大的,所以这里找一个好的切入点变得非常重要。正所谓,前人栽树,后人乘凉,参考 彻底理解Android Binder通信架构 此文,切入点是startService开始的,具体分析的是客户端进程调用服务端进程的过程。
[=> ActivityManagerNative::ActivityManagerProxy]
public ComponentName startService(IApplicationThread caller, Intent service,
String resolvedType, int userId) throws RemoteException
{
//这一步操作主要实例了两个Parcel对象,datat是客户端的发送数据,reply是服务端返回的数据
//具体参考:Parcel的obtain和recycle方法
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(IActivityManager.descriptor);
data.writeStrongBinder(caller != null ? caller.asBinder() : null);
service.writeToParcel(data, 0);
data.writeString(resolvedType);
data.writeInt(userId);
//
mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
reply.readException();
ComponentName res = ComponentName.readFromParcel(reply);
//具体参考:Parcel的obtain和recycle方法
data.recycle();
reply.recycle();
return res;
}
Parcel的obtain和recycle方法
- 这两个方法看名字很熟悉有没有?前次讲的handler中生成的Message其实也有这两个方法,知觉告诉我,这两处地方的原理是一样的,心动不如行动,马上进入验证环节。
obtain方法
[=> Parcel.java::obtain]
private static final int POOL_SIZE = 6;
private static final Parcel[] sOwnedPool = new Parcel[POOL_SIZE];
...
...
...
/**
*说明下:这个pool是上面声明的一个6容量的Parcel数组,方法中省略若干代码
*从这个pool中检索一个新的 Parcel 对象
*/
public static Parcel obtain() {
final Parcel[] pool = sOwnedPool;
synchronized (pool) {
Parcel p;
for (int i=0; i<POOL_SIZE; i++) {
p = pool[i];
if (p != null) {
pool[i] = null;
return p;
}
}
}
//这里参数传的是0
return new Parcel(0);
}
...
...
...
private Parcel(int nativePtr) {
init(nativePtr);
}
private void init(int nativePtr) {
if (nativePtr != 0) {
mNativePtr = nativePtr;
mOwnsNativeParcelObject = false;
} else {
//nativeCreate是个native方法,参考:nativeCreate方法,mNativePtr这个是可以理解成指针
mNativePtr = nativeCreate();
mOwnsNativeParcelObject = true;
}
}
private void freeBuffer() {
if (mOwnsNativeParcelObject) {
nativeFreeBuffer(mNativePtr);
}
}
- obtain方法:如果缓存Parcel数组不为空则使用缓存数组,否则自家创建一个Parcel。
nativeCreate方法
[=> android_os_Parcel.cpp::android_os_Parcel_create]
/**
*这里来看的话,很清楚的看到返回的是parcel的指针
**/
static jint android_os_Parcel_create(JNIEnv* env, jclass clazz)
{
Parcel* parcel = new Parcel();
return reinterpret_cast<jint>(parcel);
}
recycle方法
[=> Parcel.java::recycle]
/**
* 把一个Parcel对象放回到pool池中。在这回调之前,不需要这个对象。You must not touch
* the object after this call.
*/
public final void recycle() {
//这个方法内部调的是native方法,其实是根据指针释放内存
freeBuffer();
final Parcel[] pool;
//这里其实是在obtain无参方法中实例化创建过程中赋值为true
if (mOwnsNativeParcelObject) {
pool = sOwnedPool;
} else {
//mNativePtr可以看做是指向Parcel对象的指针,sHolderPool也是一个6容量的Parcel数组
mNativePtr = 0;
pool = sHolderPool;
}
//放到缓存数组中
synchronized (pool) {
for (int i=0; i<POOL_SIZE; i++) {
if (pool[i] == null) {
pool[i] = this;
return;
}
}
}
}
...
...
...
private void freeBuffer() {
if (mOwnsNativeParcelObject) {
nativeFreeBuffer(mNativePtr);
}
}
- recycle方法:根据mOwnsNativeParcelObject的值,若为true,则不带参数的obtain方法获取的对象,把其放入sOwnedPool中,否则带nativePtr的obtain(int obj)方法获取nativePtr指向的对象,把其放入sHolderPool中。
看了源码后发现,其实这个原理类似的,缓存的方式有区别,Message是通过链表方式来进行,Parcel是通过固定的数组,异曲同工之妙。
探秘mRemote
涉及代码:需要解决两个问题,一个是mRemote哪里来,另一个是mRemote的transact方法
mRemote.transact(START_SERVICE_TRANSACTION, data, reply, 0);
mRemote哪里来
- 搜寻发现,
ActivityManagerProxy
类的构造方法进行赋值,而ActivityManagerProxy(AMP)
这个类又被asInterface
方法调用。
[-> ActivityManagerNative.java::asInterface]
public abstract class ActivityManagerNative extends Binder implements IActivityManager {
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
//此处obj = BinderProxy, descriptor = "android.app.IActivityManager";
IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) { //此处为null
return in;
}
return new ActivityManagerProxy(obj);
}
...
}
此时obj为BinderProxy对象, 记录着远程进程system_server中AMS服务的binder线程的handle.
[-> ActivityManagerNative.java::ActivityManagerProxy]
class ActivityManagerProxy implements IActivityManager
{
public ActivityManagerProxy(IBinder remote){
mRemote = remote;
}
}
可知mRemote便是指向AMS服务的BinderProxy对象。
mRemote的transact方法
[-> Binder.java::BinderProxy]
final class BinderProxy implements IBinder {
public native boolean transact(int code, Parcel data, Parcel reply,
int flags) throws RemoteException;
mRemote实际上调用了 BinderProxy
的 transact
方法,而transact
调用了native方法mRemote.transact()
方法中,经过jni调用android_os_BinderProxy_transact
方法。
android_os_BinderProxy_transact
[-> android_util_Binder.cpp]
static jboolean android_os_BinderProxy_transact(JNIEnv* env, jobject obj,
jint code, jobject dataObj, jobject replyObj, jint flags)
{
...
//将java Parcel转为c++ Parcel
Parcel* data = parcelForJavaObject(env, dataObj);
Parcel* reply = parcelForJavaObject(env, replyObj);
//gBinderProxyOffsets.mObject中保存的是new BpBinder(handle)对象
IBinder* target = (IBinder*) env->GetLongField(obj, gBinderProxyOffsets.mObject);
...
//此处便是BpBinder::transact()
status_t err = target->transact(code, *data, reply, flags);
...
//最后根据transact执行具体情况,抛出相应的Exception
signalExceptionForError(env, obj, err, true , data->dataSize());
return JNI_FALSE;
}
gBinderProxyOffsets.mObject中保存的是BpBinder对象, 这是开机时Zygote调用AndroidRuntime::startReg方法来完成jni方法的注册.
其中register_android_os_Binder()过程就有一个初始并注册BinderProxy的操作,完成gBinderProxyOffsets的赋值过程. 接下来就进入该方法.
后续分析==
参考资料
一篇文章了解相见恨晚的 Android Binder 进程间通讯机制
以上是关于Binder进程间通信详解的主要内容,如果未能解决你的问题,请参考以下文章
Android(IPC)进程间通讯1:详解Binder由来?