1. 概述java
学姐今天复习了下Android进程间通讯方式,打算从基本概念、使用缘由、基本使用、原理分析几个方面来说讲。android
想要对进程间通讯方式有个相对全面的了解,就先从几个比较重要的概念IPC、AIDL、Binder提及吧。缓存
(1)IPC:Inter-Process Communication,即进程间通讯安全
(2)AIDL:Android Interface Definition Language,即Android接口定义语言。Client和Service要实现 ide
跨进程通讯,必须遵循的接口规范。须要建立.aidl文件,外在表现上和Java中的interface有点相似。性能
(3)Binder:Android进程间通讯是经过Binder来实现的。远程Service在Client绑定服务时,会在onBind()的回调中返回一个Binder,当Client调用bindService()与远程Service创建链接成功时,会拿到远程Binder实例,从而使用远程Service提供的服务。this
2. 为何使用Binder?.net
下面内容学姐参考别人的博文,进行了总结。代理
Linux系统进程间通讯方式:code
(1)传统机制。如管道(Pipe)、信号(Signal)和跟踪(Trace),适用于父子进程或兄弟进程,其中命名管道(Named Pipe),支持多种类型进程
(2)System V IPC机制。如报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)
(3)Socket通讯
然而,以上方式在性能和安全性方面存在不足:
(1)性能方面。管道和队列采用存储转发方式,数据从发送方缓存区->内核缓存区->接收方缓存区,会经历两次拷贝过程;共享内存无拷贝但控制复杂;Socket传输效率低下,且链接的创建与中断有必定开销。
(2)安全性方面。传统IPC方式没法得到对方进程可靠的UID和PID,没法鉴别对方身份,若采用在数据包里填入UID/PID的方式,容易被恶意程序利用;传统IPC方式的接入点是开放性的,没法创建私有通道,容易被恶意程序猜想出接收方地址,得到链接。
而Binder是基于C/S通讯模式,传输过程只须要一次拷贝,且为Client添加UID/PID身份,性能和安全性更好,所以Android进程间通讯使用了Binder。
3. 基本使用
假设本地Client须要使用远程Service的计算器功能。
(1)新建Client和远程Service两个Android工程。
(2)在远程Service工程中,建立ICalculator.aidl文件,并建立CalculatorService类。
(3)在Client工程中,拷贝以上.aidl文件及目录,使用Service提供的服务。
(4)启动Client和Service工程,便可实现进程间通讯。
4. 原理分析
因为Binder机制涉及的东西不少。学姐本文并不打算深刻到内核源码,关于Client是怎样获取到远程Binder的会在后续文章再讲述。下面主要是从应用层角度分析。
咱们先看看ICalculator.aidl编译生成的ICalculator.java文件。
在基本使用部分,咱们能够看到,Client在与Service创建链接成功后,会拿到远程Binder实例(此处不能直接使用远程Binder缘由还不是很清楚),并调用Stub的asInterface方法将其转换成ICalculator接口。
mCalculator = ICalculator.Stub.asInterface(service);
这一步执行的操做是:根据接口描述符,从Binder中本地查找对应的接口,如有则直接返回;不然,将Binder对象传给本地代理Stub.Proxy对象,并返回本地代理,由本地代理来接管相应的服务,Proxy也实现了ICalculator接口。
这一步以后,Client就可使用远程Service提供的服务了。
再看看onClick()事件中的加法操做。
result = mCalculator.add(2, 1);
mCalculator即为上面获得的本地代理对象,其add()的实现是
@Override public int add(int a, int b) throws android.os.RemoteException { android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); int _result; try { _data.writeInterfaceToken(DESCRIPTOR); _data.writeInt(a); _data.writeInt(b); mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0); _reply.readException(); _result = _reply.readInt(); } finally { _reply.recycle(); _data.recycle(); } return _result; }
能够看到其实是调用链接创建成功后的远程Binder的transact(add)方法。再看看Binder.transact实现
public final boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException { if (false) Log.v("Binder", "Transact: " + code + " to " + this); if (data != null) { data.setDataPosition(0); } boolean r = onTransact(code, data, reply, flags); if (reply != null) { reply.setDataPosition(0); } return r; }
能够看到最后调用了远程Binder的onTransact()方法,也就是走到了远程Binder的Stub.onTransact()方法。这个方法实现是
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException { switch (code) { case TRANSACTION_add: { data.enforceInterface(DESCRIPTOR); int _arg0; _arg0 = data.readInt(); int _arg1; _arg1 = data.readInt(); int _result = this.add(_arg0, _arg1); reply.writeNoException(); reply.writeInt(_result); return true; } ... } }
这里最终调用到了远程Service工程建立的ICalculator.Stub实例mBinder的add()方法即a+b,所以获得了最后的结果。
好了,咱们再来梳理下,Client得到并使用Service服务的过程:
(1)Client与Service创建链接,获得远程Binder(这个Binder就是Service的onBind返回的Binder,可是客户端不能直接使用,具体缘由还不是很明确)。
(2)将远程Binder传给本地代理,获得本地代理Stub.Proxy实例。
(3)经过本地代理Stub.Proxy实例间接调用远程Binder对象的add()方法。 具体实现是:因为远程Binder已经传给了本地代理Stub.Proxy。那么经过本地代理Stub.Proxy实例间接调用远程Binder的transact(TRANSACTION_add)操做;调用远程Binder的onTransact()方法;调用远程Binder的实现类ICalculator.Stub的add()方法
(4)获得结果
下面再总结下ICalculator、Stub、Proxy这3个类的关系:
ICalculator:就是一个接口类。内部包括以前的接口方法和静态Stub类。
Stub:远程Binder实现类。继承类Binder和ICalculator接口。
Proxy:远程Binder代理类。实现ICalculator接口。
5. 总结
(1)Binder至关于不一样进程间数据通讯的通道
(2)核心是代理模式,使用本地代理Proxy操做远端Binder,使用相应服务
(3)如理解有误,欢迎指正
6. 参考连接
http://blog.csdn.net/singwhatiwanna/article/details/19756201
http://blog.csdn.net/luoshengyang/article/details/6618363