前言

binder通信在Android中一直属于核心机制,前面分析了binder的c层和C++层的通信机制,但是没有分析binder的java层通信机制,但是作为一个资深Android开发人员,这个怎么能不搞懂,这补充一下;

首先要声明一下,Android7.00和8.0的源码其实差别还有点的,binder通信上主要表现在7.0的AMS是继承ActivityManagerNative的,而8.0的AMS是直接实现了IActivityManager.stub接口,也就是直接通过AIDL接口实现的,其实原来的ActivityManagerNative的实现就是一个stub的功能。

三个“搞明白”

想要知道AIDL的底层原理,首先要来三个"搞明白":
(1)搞明白AIDL编译时期生成的文件其中的类和方法的作用
(2)搞明白Java层binder、JavaBBinder、JavaBBinderHolder之间的关系,以及他们的分工和作用
(3)搞明白Parcel在binder机制中的作用

搞明白AIDL编译时期生成的文件其中的类和方法的作用

AIDL是Android interface definition language,因此使用的时候是面向接口的,需要自定义一个接口,例如:

interface IStoreAidl {int sell(String pencil);
}
public interface IStoreAidl extends android.os.IInterface{(1)public static abstract class Stub extends android.os.Binder implements com.xx.leo_service.ILeoAidl{(2)private static final java.lang.String DESCRIPTOR = "com.android.test.IStoreAidl";(2-1)public android.os.IBinder asBinder(){......}(2-2)public static com.xx.leo_service.ILeoAidl asInterface(android.os.IBinder obj){...... return new com.xx.leo_service.ILeoAidl.Stub.Proxy(obj);}(2-3)public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)){switch (code){case INTERFACE_TRANSACTION:{reply.writeString(DESCRIPTOR);return true;}case TRANSACTION_sayhello_to:{data.enforceInterface(DESCRIPTOR);java.lang.String _arg0;_arg0 = data.readString();int _result = this.sell(_arg0);reply.writeNoException();reply.writeInt(_result);return true;}}return super.onTransact(code, data, reply, flags);}(2-4)private static class Proxy implements com.xx.leo_service.ILeoAidl{}(2-5)}
}

在编译的时候,自动生成AIDL文件,文件中包含三个部分:
(1)自定义接口IStoreAidl自动继承IInterface接口
(2)自动创建抽象类Stub继承Binder并实现IStoreAidl接口,对于客户端来说,stub代表了服务端,服务端继承了stub并实现接口中的方法,客户端通过暴露出的接口调用服务端的方法,Android7.0中的AcrivityManagerNative就扮演了这个角色。
(3)stub中有五个重要的部分:

  • DESCRIPTOR(2-1):定义一个常量描述符,一般用来作为token通过parcel写给binder,binder为啥安全,就因为他们有校验机制
  • asBinder()(2-2):通过parcel实现asBinder()方法,服务端通过该方法来获取Ibinder;
  • asInterface(IBinder iBinder)(2-3):客户端通过该方法得到服务端的代理,proxy就相当于stub在客户端的一个代理,客户端通过这个代理,并其中的方法,将数据封装成功parcel然后写入到binder底层驱动中,然后发送到服务端;
  • onTransact()(2-4):重写onTransact()方法,该方法是binder的方法,用来分发onTransact的,在这里调用服务端实现的方法,并将结果写入parcel的reply中去通过该方法层层传递,返回给客户端;
  • Proxy(2-5):stub的静态内部类,实现了IStoreAidl接口,是服务端stub在客户端的一个代理类,主要用来将客户端的消息封装成parcel数据,然后通过remote.transact()与binder驱动进行交互

搞明白Java层binder、JavaBBinder、JavaBBinderHolder之间的关系,以及他们的分工和作用

首先通过一幅图总结一下:

public class StoreService extends IStoreAidl.Stub {......}(1)
ServiceManager.addService("hello", new HelloService())(2)

(1)服务端是继承的IStoreAidl.Stub 其实也就是继承的Binder,因此服务端就是个Binder
(2)从关系图中可以看出,new HelloService()的时候,在Binder的构造中调用init(),调用native层的方法创建JavaBBinderHolder()并将它赋值给Binder的mObject;
(3)JavaBBinderHolder的构造中又创建了JavaBBinder extends BBinder,并赋值给了mBinder
(4)最后又把Binder赋值给了JavaBBinder的mObject
(5)将服务注册到ServiceManager中去,因此这个注册到SM中的Binder,就是JavaBBinder它属于BBinder
因此binder、JavaBBinder、JavaBBinderHolder他们是相互持有,JavaBBinder属于BBinder,IPCThreadState从binder驱动中读取到数据后,调用了注册到SM中的服务端,也就是BBinder

status_t BBinder::transact(uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{.....
//实际上就是通过onTransact分发,调用了JavaBBinder中的onTransact方法
//JavaBBinder通过ExecTransact方法分发到了java层binder的onTransact()方法,也就是IHelloService.Stub重写的onTransact()方法,将文章开始部分
err = onTransact(code, data, reply, flags);
....return err;
}

JavaBBinder中重写了onTransact,其实就是通过ExecTransact方法,继续向java层的binder中的onTransact分发,其实就是调用了IHelloService.Stub中的onTransact()方法,然后再那里调用服务端的接口实现代码:

import android.util.Slog;
public class HelloService extends IHelloService.Stub {private static final String TAG = "HelloService";public int sell(java.lang.String shoe) throws android.os.RemoteException {Slog.i(TAG, "sell "+shoe);return 100;}
}

搞明白Parcel在binder机制中的作用

java层的parcel其实就是对C层Parcel的一个封装,Parcel在binder机制中的作用除了能够封装数据之外,主要是因为他有传递binder对象的功能:
parcel.writeStrongBinder():flatten_binder
ReadStringBinder():unFlatten_binder

这两个方法对binder对象进行序列化和反序列化;

parcel.writeStrongBinder(service)

我们在之前讲到,这个service是我们自己的服务,也就是BinderrProxy——>JavaBBinder::BBinder
java层的writeStrongBinder其实就是调用native的android_os_Parcel_writeStrongBinder

static void android_os_Parcel_writeStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr, jobject object)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {const status_t err = parcel->writeStrongBinder(ibinderForJavaObject(env, object));if (err != NO_ERROR) {signalExceptionForError(env, clazz, err);}}
}sp<IBinder> ibinderForJavaObject(JNIEnv* env, jobject obj)
{   //gBinderOffsets.mClass指向BinderProxy——>JavaBBinder::BBinderif (env->IsInstanceOf(obj, gBinderOffsets.mClass)) {JavaBBinderHolder* jbh = (JavaBBinderHolder*)env->GetLongField(obj, gBinderOffsets.mObject);return jbh != NULL ? jbh->get(env, obj) : NULL;}if (env->IsInstanceOf(obj, gBinderProxyOffsets.mClass)) {return (IBinder*)env->GetLongField(obj, gBinderProxyOffsets.mObject);}return NULL;
}

所以这个parcel->writeStrongBinder(ibinderForJavaObject(env, object));其实就是parcel->writeStrongBinder(new JavaBBinder(env, obj));

status_t Parcel::writeStrongBinder(const sp<IBinder>& val)
{return flatten_binder(ProcessState::self(), val, this);
}
status_t flatten_binder(const sp<ProcessState>& /*proc*/,const sp<IBinder>& binder, Parcel* out)
{flat_binder_object obj;if (IPCThreadState::self()->backgroundSchedulingDisabled()) {obj.flags = FLAT_BINDER_FLAG_ACCEPTS_FDS;} else {obj.flags = 0x13 | FLAT_BINDER_FLAG_ACCEPTS_FDS;}if (binder != NULL) {IBinder *local = binder->localBinder(); //binder是BBinder,所以得到的时this,所以走的elseif (!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);}} else {obj.type = BINDER_TYPE_BINDER;obj.binder = 0;obj.cookie = 0;}return finish_flatten_binder(binder, obj, out);
}

所以服务端的BinderProxy——>JavaBBinder()::BBinder也就被拆分成了type、binder和cookie存到了parcel中

parcel.ReadStringBinder(service)

read其实就是write的逆向走势;
readStrongBinder其实就是调用native层的nativeReadStrongBinder方法,调用C++层的readNullableStrongBinder——>unflatten_binder方法;

static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {//readStrongBinder调用了Parcel中的unflatten_binder方法return javaObjectForIBinder(env, parcel->readStrongBinder());}return NULL;
}
status_t unflatten_binder(const sp<ProcessState>& proc,const Parcel& in, sp<IBinder>* out)
{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: //这里是从服务端获取,因此走的这个分支//因为ServiceManager注册的时候,也调用了ProcessState.getStrongProxyHander()方法,返回new BpBinder(0)//但是new BpBinder(0)的0是SM专属的,因此这里返回的是new BpBinder(handler),handler肯定不为0*out = proc->getStrongProxyForHandle(flat->handle);return finish_unflatten_binder(static_cast<BpBinder*>(out->get()), *flat, in);}}return BAD_TYPE;
}

因为ServiceManager注册的时候,也调用了ProcessState.getStrongProxyHander()方法,返回new BpBinder(0),0是SM专属的,因此这里返回的是new BpBinder(handler),handler肯定不为0;所以

sp<IBinder>* out = proc->getStrongProxyForHandle(flat->handle);
相当于
sp<IBinder>* out = new BpBinder(handler);
所以:
static jobject android_os_Parcel_readStrongBinder(JNIEnv* env, jclass clazz, jlong nativePtr)
{Parcel* parcel = reinterpret_cast<Parcel*>(nativePtr);if (parcel != NULL) {return javaObjectForIBinder(env, new BpBinder(handler));}return NULL;
}
jobject javaObjectForIBinder(JNIEnv* env, const sp<IBinder>& val)
{//如果是服务端的binder也就是BBinder就返回JavaBBinder,这里显然不是了if (val->checkSubclass(&gBinderOffsets)) {jobject object = static_cast<JavaBBinder*>(val.get())->object();return object;}AutoMutex _l(mProxyLock);//如果之前已经获取过,就返回BinderProxy,第一次的话肯定是没有获取过的jobject object = (jobject)val->findObject(&gBinderProxyOffsets);if (object != NULL) {jobject res = jniGetReferent(env, object);if (res != NULL) {return res;}android_atomic_dec(&gNumProxyRefs);val->detachObject(&gBinderProxyOffsets);env->DeleteGlobalRef(object);}//如果是第一次进入,就创建一个BinderProxy并将new BpBinder(handler)对象赋值给BinderProxy的objectobject = env->NewObject(gBinderProxyOffsets.mClass, gBinderProxyOffsets.mConstructor);if (object != NULL) {env->SetLongField(object, gBinderProxyOffsets.mObject, (jlong)val.get());val->incStrong((void*)javaObjectForIBinder);jobject refObject = env->NewGlobalRef(env->GetObjectField(object, gBinderProxyOffsets.mSelf));val->attachObject(&gBinderProxyOffsets, refObject,jnienv_to_javavm(env), proxy_cleanup);sp<DeathRecipientList> drl = new DeathRecipientList;drl->incStrong((void*)javaObjectForIBinder);env->SetLongField(object, gBinderProxyOffsets.mOrgue, reinterpret_cast<jlong>(drl.get()));android_atomic_inc(&gNumProxyRefs);incRefsCreated(env);}return object;
}

所以当通过Parcel的unflatten_binder方法拿到远程服务的binder对象(new BpBinder(handler)handler非0)后,然后封装成java层客户端的代理BinderProxy()对象返回给客户端
调用链太长用时序图总结一下:

最后通过之前讲的IstoreAidl.stub.asInterface(BinderProxy())方法得到IstoreAidl.stub.Proxy对象,且BinderProxy中的remote属性指向new BpBinder(handler)

AIDL底层原理需要搞明白的三个点相关推荐

  1. KVO-基本使用方法-底层原理探究-自定义KVO-对容器类的监听

    书读百变,其义自见! 将KVO形式以代码实现呈现,通俗易懂,更容易掌握 :GitHub   -链接如果失效请自动搜索:https://github.com/henusjj/KVO_base 代码中有详 ...

  2. 为什么使用HashMap需要重写hashcode和equals方法_最通俗易懂搞定HashMap的底层原理...

    HashMap的底层原理面试必考题. 为什么面试官如此青睐这道题? HashMap里面涉及了很多的知识点,可以比较全面考察面试者的基本功,想要拿到一个好offer,这是一个迈不过的坎,接下来我用最通俗 ...

  3. 一文搞明白GIT——Git原理解析与常用命令

    工作中经常用git,但是不少命令经常使用出现各种各样的问题,也不太理解其中的原理.今天专门总结一下git的原理,理解原理之后想实现什么样的功能直接找相应的命令即可.如有错误和不足,欢迎指正! 一. 工 ...

  4. 一文彻底搞懂事务底层原理

    事务底层原理(INNODB) 前言 redo log 为什么需要 redo log 一些问题 重做日志结构 重做日志文件结构 log group与循环写入 日志何时写入磁盘? 数据恢复:LSN标记 C ...

  5. 后端开发程序员须彻底搞懂的 IO 底层原理

    一.混乱的 IO 概念 IO是Input和Output的缩写,即输入和输出.广义上的围绕计算机的输入输出有很多:鼠标.键盘.扫描仪等等.而我们今天要探讨的是在计算机里面,主要是作用在内存.网卡.硬盘等 ...

  6. 大三专科实习第一个月——HTTPS底层原理详解

    简介:脱变从现在开始,以上文章讲述的是广度问题接下来方向将是深度的问题.觉得我还可以的可以加群探讨技术QQ群:1076570504 个人学习资料库http://www.aolanghs.com/ 微信 ...

  7. 搞定高并发,岂能不懂Synchronized底层原理?

    Synchronized 是 Java 中解决并发问题的一种最常用的方法,也是最简单的一种方法.本文作者将全面剖析 Synchronized 的底层原理. Synchronized 的基本使用 Syn ...

  8. mysql哨兵机制_Redis 哨兵机制以及底层原理深入解析,这次终于搞清楚了

    前面我们基于实际案例搭建了缓存高可用方案(分布式缓存高可用方案,我们都是这么干的)同时提到了redis主从架构下是如何保证高可用的,讲到了它是通过redis sentinel的机制来实现的. 今天我们 ...

  9. 大三专科实习第一个月——Socket底层原理详解与应用

    简介:脱变从现在开始,以上文章讲述的是广度问题接下来方向将是深度的问题.觉得我还可以的可以加群探讨技术QQ群:1076570504 个人学习资料库http://www.aolanghs.com/ 微信 ...

最新文章

  1. TIOBE 1月编程语言排行榜:C语言再度「C 位」出道,Python惜败
  2. 软考培训 - 上海2班开课剪影
  3. @service注解_Spring 中 @Component、@Service 等注解如何被解析的
  4. 安卓手机可以用python编程软件-可以在手机上进行Java,Python的编程软件,你用过么?...
  5. 互联网医院 2020年突出成就_【关注】中国社科院发布2020中国医院互联网影响力排行榜...
  6. 信息学奥赛一本通 1311:【例2.5】求逆序对 | 1237:求排列的逆序数 | OpenJudge NOI 2.4 7622:求排列的逆序数 | 洛谷 P1908 逆序对
  7. SVN历史版本比较中文乱码
  8. oppo 手机侧滑快捷菜单_[图]OPPO又出脑洞设计:弹出/侧滑第二块手机屏幕
  9. php中svn上传项目直接访问不了,phpstorm8 通过svn导入项目后项目右键列表里没有subversion选项,无法提交和更新啊?...
  10. Hibernate持久化对象的三种状态深入理解
  11. 心在哪裡行動力就在那裡 戴晨志
  12. 【LaTex使用总结】LaTex,pdflatex,xelatex,xetex等的区别和关系
  13. acs cisco 查看log_Cisco ASA 5510 防火墙 配置笔记
  14. CSDN博客之星评选
  15. java中类成员,java中类成员的限定词
  16. 清除某个特定网站的缓存---基于Chrome浏览器
  17. 装自己的服务器(教程)yum-jdk-mysql-防火墙-SVN-redis-申请域名
  18. 微信小程序实现轮播图(超简单)
  19. 你的电脑被黑客黑过吗?
  20. 多吃巧克力多笑脑子会更聪明

热门文章

  1. 安装Microsoft Office
  2. Android Studio 抽卡小游戏
  3. 微信小程序 实现导航守卫
  4. TodoMVC模板的原生js待办事项卡片
  5. 如何用PS修改证件照的背景颜色
  6. Vue中使用quillEditor编辑器使用粘贴可用,小坑讲解,回显空格丢失等问题。
  7. coreldraw2019天气滤镜_coreldraw2019(图形图像处理工具)
  8. 苹果6怎么显示itunes store无法连接服务器,苹果iphone6无法连接到itunes+store怎么办 解决方法...
  9. uniapp仿美团小程序左上角的位置定位
  10. 【企业数字化转型】数据可视化技术:Three.js 用Physijs在场景中添加物理效果