系列目录 https://blog.csdn.net/jzlhll123/article/category/7671581
引用了很多网上的图片,具体链接未能及时保存,致歉。
20180625 再探binder https://blog.csdn.net/jzlhll123/article/details/80806584
在学习Binder的过程中有很多迷惑和难度。而且app开发很少接触到这个。翻看了无数的帖子,3-4本PDF/书,都是上来就将驱动的binder,然后将c++,再将到java层。诚然这是合理的架构设计顺序。但是对于java开发人员就痛苦了。我习惯的是从用法->原理的理解过程。
我建议android app开发人员,学习过程按照一层一层往下。在不熟悉下一层的时候没关系,就当它是黑盒,这也是面向对象的思想,抽象后的对象(比如ActivityManagerService,ServiceManager等)我们都可以当它为黑盒。
根据我踩坑后得到的浅浅经验,建议纯app开发人员如此开始:
第一步,AIDL
阅读之前请结合自己实现AIDL和AIDL使用和原理,先从AIDL学起的使用,自己生成一个AIDL文件,分析AIDL自动生成的代码。
package com.service;
interface IMyRemote {
void func(String info);
}
学完以后,大概能理解到android机制系列之一 AIDL实现原理这个层次。
第一步,学到
AIDL
1. 接口IMyRemote
继承IInterface,把方法添加了远程异常;
2. 内部类Stub(故意写成MyStub)继承Binder,初始化, asInterface转换接口,和处理回调onTransact();
3. MyProxy实现IMyRemote,将Ibinder封装成具体同名方法transcat()给底层传递数据。
使用层面来讲,只要asInterface()后就是具体的接口可以正常使用了。
Service
onBinder() return IInterface回去binder对象,在自动代码中会attachInterface()保留该实体对象
Client
onServiceConnected() 会返回IBinder对象(mRemote#1), Client拿到后,需要asInterface()->queryLocalInterface()转成我们需要的代理对象(不同进程)或实体对象(同进程)
通信过程
同进程实体方法调用: 同一进程空间,直接调用;
不同进程代理对象方法调用:
client将调用方法,参数_data和返回结果_reply序列化,mRemote#1(注意是Ibinder)transact(code, _data, _reply, flag)到黑盒Binder机制里面去,挂起;
service收到黑盒Binder机制回调给Stub的onTransact(code, data, reply, flag) , 将data读取出来执行,如果有返回值就填充reply,然后结束,唤醒client;
client唤醒后,如果有返回值,就将_reply反序列化,拿到结果。
20180622补充说明:
这是由bindService后得到的Ibinder决定的,绑定后,同进程将直接拿到实例对象,同你service保存的对象,调用的方法也就是直接使用func(); 而不同进程,Ibinder实际上BinderProxy, 因此func()分装了JNI transact()动作来传达到server端最终调用onTransact()
第一步,黑盒:
Binder.java是怎么运作的?
context.bindService(intent, serviceConnection, flag)怎么个流程?
这个serviceConnection给谁去运作和怎么回调的?
第二步,bindService
, onBind
和onServiceConnected
的过程
继续当Binder.java提供的方法为黑盒。暂时我们知道transact, onTransact的用处就够。我们一般bindService会在Activity/service中绑定。我们先看2张类图。
理解下为何会使用wrapper(装饰/包装)模式,继承父类,又传入父类。查看源码ContextWrapper相当于完全照抄了一遍一些跟应用相关的方法,并可以初始化或者重设mBase。设想如果我们采用继承方式来做会丢失如下2点优势。
好处有2点,第一,扩展(目前来说没有扩展)但不能修改父类;第二,可以方便的替换掉Impl类,而不用影响ContextWrapper的子类。
不懂不要紧,我们来稍微类比下AIDL:
第一步中自动实现的AIDL的IManager整个大类,就相当于这里的IActivityManager,实现了IInterface;
ActivityManagerNative相当于我们AIDL的Stub类,内部可以拿到ActivityManagerService实现的接口,实现了OnTract(),外加Proxy代理类;
该类类图:
所以,ActivityManagerNative.getDefault().xxx(),其实都是跨进程调用(如果它自己则不用)到ActivityManagerService的具体实现。而这里并没有超出我们学习AIDL相关的范畴。因此,暂时不深入研究Binder,我们目前只要知道transact()和onTransact()是谁调用,在哪里用的即可,而且我们已经掌握。
好了,这个时候,我们已经追到AMS的bindService了:然后学习Android服务之bindService源码分析。
里面很多的LoadedApk,ActivityThread,ActivityManagerService,ActivityServices等跳来跳去,一时之间很难理清楚。模模糊糊我们有了一些概念,留着后续继续学习,这里留一个黑盒。因为Activity,Service的启动流程等等都会涉及到这里。参考:https://blog.csdn.net/zhangfei2018/article/details/46518615
第二步,学到:
我自己画的这张图, 而且是真的理解了。注意进程的关系,和子类归属关系。虚线是跨进程,实线是父类方法或者成员变量的方法。
ContextImpl相当于Activity/Service的父类(本质上是包装过的);
ActivityManagerNative其实是静态类,我们拿到的静态对象去bindService的时候,其实还是在client进程;
ActivityThread是每一个应用程序都需要通过标准java的main()函数创建出来的, 并且每个Service都有一个ActivityThread的成员变量(即我们常说的主线程);
ActivityManagerService是SystemServer进程里的,他也有一个标记为system的ActivityThread;
所以在这里面涉及到3个进程,client的activity/service bindService的地方, service提供IBinder的地方以及framework的AMS。那么, 可以大致看出ActivityManagerService维护了一个所有应用层服务的表,client启动的时候添加进去;当client通过binder绑定的时候, 从表中找到对应intent的service,将serviceConnection封装进去,返回Ibinder,最后又通过Binder转达到client。
回来来看,我们是想了解Binder,但是事实上,Binder的用法在上述流程中并没有太多的变化。所以回头来,再次巩固下java层的Binder知识。
一个接口实现类
:实现 IInterface;如 IActivityManager, IServiceManager, 如AIDL文件,如;
一个主类
: 继承Binder; 如AIDL的Stub类,如ActivityManagerNative;
一个代理类
,一般是主类内部类:实现接口实现类:如AIDL Stub内的Proxy类,如ActivityManagerNative内的ActivityManagerProxy类;
主类方法具体实现
:真正的实现方法,如ActivityManagerNative的ActivityManagerService,如AIDL的我们在service中实现的Stub的实例;
主类Binder驱动回调方法
onTransact():将Binder调过来的指令调用上述具体实现,并返回;
Proxy类方法抽象实现
:将同名方法,使用binder驱动transact()传递指令同步等待结果。
至此,Java层的binder使用和阅读framework java层,在多次折腾下,基本算是懂了个大概。
第二步,留下的黑盒:
Binder底层机制;
ActivityManagerService,ActivityThread,Activity启动过程,Service启动过程,Application启动过程…
第三步 Binder C++使用学习:
TODO
第四步 其他帖子的学习:
只有到了这个阶段,再去看后面引用的几篇文章才有意义!, 否则App开发工程师,上来就是驱动,很吃力。
引用贴说的好,学习顺序。目前我已经达到了3阶段水平了。对于app工程师,基本能够比较轻松的继续研究Activity启动流程,广播机制,ContentProvider机制等framework机制就够了。否则,Binder学1个月也不能完成。
引用灰太狼一句话:我还会回来的
。
- 先学会熟练使用AIDL进行跨进程通信(简单来说就是远程Service)
- 看Android文档,
Parcel, IBinder, Binder
等涉及到跨进程通信的类- 不依赖AIDL工具,手写远程Service完成跨进程通信
- 看《Binder设计与实现》
- 看老罗的博客或者书(书结构更清晰)
- 再看《Binder设计与实现》
- 学习Linux系统相关知识;自己看源码。
背景知识:
进程间是隔离的,进程间,虚拟地址空间,无法共享数据;所以需要IPC,性能上讲,相对较少,一次内存拷贝,其他的一般2次;共享内存不用拷贝,但是很复杂难以使用,也不利于安全;socket适合网络通信或者一些低速的通信;安全上讲,传统linux的IPC相对来说很少涉及UID/PID,一些权限管理不够安全;
Binder驱动,内核模块虚拟成一种硬件设备,操作不同进程的内存;
在阅读Binder的通信模型的时候,经常会被他们提到
Binder驱动,ServiceManager,Server,Client
四个角色。serviceManager进程向binder驱动申请成为0号管理者;
其他Server向serviceManager注册;
Client通过serviceManager获取具体Server的proxy引用对象。
这里的Server并不是我们平时使用的组件service(https://blog.csdn.net/sinat_26158619/article/details/51184927)。binder提供了实名server和匿名server,framework java层使用了匿名server的技术,实现了组件service,https://blog.csdn.net/zhangyongfeiyong/article/details/51953300,也可以自己去追相关的源码,可以看到bindService的时候,是通过activityManagerService查询到对应的intent的服务,最终拿到的service代理。
所以理解这里的AIDL服务service和binder server的区别。
但是,不管怎么样,如果不是同一进程,拿到的代理引用对象,都会通过binder驱动代码完成跨进程通信。
参考学习引用:
title bindService时序图
client->client: bindService:ContextWrapper
client->client: bindService:ContextImpl
client->client: bindServiceCommon:ContextImpl
client->ActivityManagerNative: bindService:ActivityManagerNative
ActivityManagerNative-->ActivityManagerService: bindService:ActivityManagerService
ActivityManagerService->ActivityManagerService: bindServiceLocked:ActivityServices
ActivityManagerService-->AcitivityThread: scheduleCreateService-->handleCreateService
ActivityManagerService-->AcitivityThread: scheduleBindService-->handleBindService
AcitivityThread->Service:onBind()
AcitivityThread->Service:onBind()
AcitivityThread-->ActivityManagerService:publishService()
ActivityManagerService->ActivityManagerService:publishServiceLocked:ActivityServices
ActivityManagerService->ActivityManagerService:doConnect handler:LoadedApk
ActivityManagerService-->client: onServiceConnected()