一:Binder介绍
Binder是一套ipc通信方案
Binder框架定义了四个角色: Server ,Client,ServiceManager (以后简称SMgr)以及Binder驱动。其中Server ,Client,SMgr运行于用户空间,驱动运行于内核空间。这四个角色的关系和互联网类似: Server是服务器, Client是客户终端, SMgr是域名服务器(DNS),驱动是路由器。 在网络通信中域名服务器的地址是一个固定的地址,所以很方便的通过这个固定地址拿到。那么在Binder中, SMgr 是一个进程, Server是另一个进程, Server向SMgr注册Binder必然会涉及进程间通信。当前实现的是进程间通信却 又要用到进程间通信,这就好象蛋可以孵出鸡前提却是要找只鸡来孵蛋。 Binder的实现比较巧妙:预先创造一只鸡来 孵蛋: SMgr和其它进程同样采用Binder通信, SMgr是Server端,有自己的Binder对象(实体),其它进程都是 Client,需要通过这个Binder的引用来实现Binder的注册,查询和获取。 SMgr提供的Binder比较特殊,它没有名字 也不需要注册,当一个进程使用BINDER_SET_CONTEXT_MGR命令将自己注册成SMgr时Binder驱动会自动为它创建 Binder实体(这就是那只预先造好的鸡)。其次这个Binder的引用在所有Client中都固定为0而无须通过其它手段获得。也就是说, 一个Server若要向SMgr注册自己Binder就必需通过0这个引用号和SMgr的Binder通信。类比网络通信, 0号引用就好比域名服务器的地址,你必须预先手工或动态配置好。要注意这里说的Client是相对SMgr而言的,一个应用程序可能是个提供服务的Server,但对SMgr来说它仍然是个Client。
BInder通信的流程梳理:
1. 首先, 一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;
2. Server 通过驱动向 ServiceManager 中注册 Binder( Server 中的 Binder 实体),表明可以对外提供服务。驱动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包传给 ServiceManager ,ServiceManger 将其填入查找表。
3. Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用。
4. 通过这个Binder实体引用, Client实现和 Server 进程的通信。
二:Binder的创建
总的来说, Binder的初始化是在进程已创建就完成了。创建进程后会第一时间为这个进程打开一个binder驱 动,并调用mmap接口向Binder驱动中申请内核空间的内存。
三:AIDL原理
.aidl文件
-----------------------
interface IServer {
int testFunction(String s);
}
远程服务
----------------------
public class RemoteTestService extends Service {
private IServer.Stub serverBinder = new IServer.Stub() {
@Override public int testFunction(String s) throws RemoteException {
Log.i("test","testFunction s= " + s);
return 0;
}
};
@Nullable @Override
public IBinder onBind(Intent intent) {
return serverBinder;
}
}
客户端
------------
public class MainActivity extends AppCompatActivity {
private IServer iServer;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iServer = IServer.Stub.asInterface(service);
System.out.println("onServiceConnected");
try {
int a = iServer.testFunction("test string");
Log.i("test", "after testFunction a:" + a);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test", "onServiceDisconnected");
}
};
@Override
protected void onCreate( Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this, RemoteTestService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
------------------------------------
aidl文件自动生成的Java文件如下:
通过aidl文件生成了Java文件,这个类的静态内部类Stub(继承了 Binder,实现了接口方法),主要逻辑在方法asInterface,如下图
asInterface方法之后也就能调用服务端端方法了,testFunction()方法进行数据的传递,有同步和异步之分。aidl接口中定义的方法都是在实现BInder的线程池中执行。详见 :aidl中的oneWay的语法解释
客户端:持有proxy
服务端:持有stub
stub是类IServer的静态内部类,proxy是stub的静态内部类通过构造方法持有stub引用
服务端和客户端同一进程:客户端可以直接使用stub类
服务端和客户端非同一进程:客户端需要获取接口类型的stub也就是Proxy,而Proxy持有stub,这个接口就是aidl
aidl通信的基本步骤如下:
1. Client通过ServiceConnection获取到Server的Binder,并且封装成一个Proxy。
2. 通过Proxy来同步调用IPC方法(
testFunction),同时通过Parcel将参数传给Binder,最终触发Binder的
transact方法。
3. Binder的transact方法最终会触发到Server上Stub的onTransact方法。
4. Server上Stub的onTransact方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将
结果写入Parcel后传回。
5. 请注意: Client的Ipc方法中,执行Binder的transact时,是阻塞等待的, 一直到Server逻辑执行结束后才会继
续执行。当然,如果IPC方法是oneWay的方式,那么就是非阻塞的等待。
6. 当Server返回结果后, Client从Parcel中取出返回值,于是实现了一次IPC调用。
所以, aidl 文件会生成一个java文件,这个java文件的意义在于将核心的binder驱动封装成为java层可以直接调用的
代码,同时也处理了将java层的数据格式转换为Parcel格式数据进行跨进程传递的一个功能。所以, aidl是一个使用binder的标准方案,该方案的代码同样的可以通过用户自己编写的方式完成
四:BindService的原理
1,client(客户端进程)
2,service (远端服务进程)
3,ams(systemservicer进程)
4,serviceManager(进程)
具体流程如下:
1)Activity作为Client发起bindService,最终会调度到AMS 去执行bindService。在这个过程中, Client要去调用
AMS的代码,所以此时就会涉及到跨进程调度,基于第三章的Binder通信模型我们不难知道, Client会先和
ServiceManager通信,从ServiceManager中拿到AMS的IBinder。
2)Activity拿到AMS的IBinder后,跨进程执行AMS的BindService函数;
3)由于AMS管理所有的应用进程,因此AMS中持有了应用进程的Binder,所以此时AMS可以发起第4步也就是跨进
程调度scheduleBindService();
4)Server端会在收到AMS的bindService的请求后,会将自己的IBinder发送给client,但是Server必须通过AMS才能
将Binder对象传过去,所以此时需要跨进程从ServiceManager中去拿到AMS的binder;
5)Server端通过AMS的binder直接调用AMS的代码publishService(),将service的Binder发送给AMS;
6)经过层层调用,最终AMS讲Server端的binder通过回调connect函数传递给了Client端的Activity;
以上就是bindService的全流程,这个流程主要的目的是将Server端的Binder对象发送给Client端。从此以后, Client端就可以通过Server端的binder与Server端像调用自己的代码一样完成跨进程通信了。
五:Binder的优点
高效:数据拷贝一次,通过mmap(内存映射)提高数据传递效率
内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间反之内核空间对这段区域的修改也能直接反应到用户空间
安全:内核添加身份识别
稳定:c/s架构
六:Java& Native层面 Binder相互转化
因此我们必须研究一下Java framework层与Native层之间的相互调动。
1)Client端通过ServiceManager 拿到Server端服务的Binder 代理,也就是BinderProxy(是Server端Binder的一个 代理);
2)这个BinderProxy的访问需要经过JNI层的Android_util_binder类将请求转交给native的BpBinder(p代表代理的意思);
3) BpBinder会通过ioctl将请求转交给Binder驱动设备;
4)在服务端注册了一个监听请求的回调函数, 一旦驱动层收到BpBinder 的调用,就会回调BBInder注册的回调函 数,于是,就将请求转给了BBinder;
5) BBinder拿到请求后,会进行一些数据的处理,然后通过JNI将请求转交给了java类;
6)java层会通过aidl中的函数将请求发送给Server端的实现者,由Server端通过stub 去调用相关的执行代码,并将 结果通过类似的路径返回。
以上只是一个大概的流程
七: ServiceManager解析
ServiceManager作为一个起始的服务,是通过init.rc 来启动的。
//system\core\rootdir\init.rc
//启动的服务 ,这里是用的服务名称。服务名称是在对应的rc文件中注册并启动的
start servicemanager
具体的服务信息是在servicemanger.rc命名并定义的
//\frameworks\native\cmds\servicemanager\servicemanager.rc
service servicemanager / system/ bin/ servicemanager
class core animation
user system
//说明以用户system身份运行
group system readproc
//说明servicemanager是系统中的关键服务,
//关键服务是不会退出的 ,如果退出了 ,系统就会重启 ,当系统重启时就会启动用onrestart关键字修饰的进程,
//比如zygote、media、 surfaceflinger等等。
critical
onrestart restart healthd
onrestart restart zygote
onrestart restart audioserver
onrestart restart media
onrestart restart surfaceflinger
onrestart restart inputflinger
onrestart restart drm
onrestart restart cameraserver
onrestart restart keystore
onrestart restart gatekeeperd
onrestart
. .
restart thermalservice