IPC(Inter-Process Communication,跨进程通信)是指两个进程之间数据交换的过程,因此我们首先必须了解什么是进程,什么是线程。

进程:进程是正在运行的程序的实例,与程序相比,它更强调动态的概念,与线程相比,进程是线程的容器,一个进程可以包含多个线程但至少包含一个线程。进程是任务调度的基本单位,是系统资源的分配单位。

线程:线程是进程中的一条执行路径,它只能隶属于某一个进程,在支持多线程的操作系统或程序设计语言中(如java),线程是任务调度的单位,但它不是系统资源分配的单位,线程完全继承父进程占有的资源,但它执行时,也有属于自己的运行现场,如PC寄存器的值,栈空间等。

关于进程与线程内存资源方面的知识请参看我的博文:http://blog.csdn.net/htq__/article/details/50822582

IPC机制不是安卓独有的,任何一个操作系统都需要IPC机制,如linux中可以通过命名管道,共享主存,信号量,Socket,消息队列等方式来呢实现IPC机制,安卓虽然是基于linux内核的,但是安卓中的IPC机制与linux不完全相同,如Binder机制就是安卓的IPC中的一大特色,这种IPC机制在linux中是不存在的。本博客将详细讲解安卓中的Binder机制。

在安卓开发中Binder主要用在Service中,包括AIDL与Messenger,而Messenger的底层仍然是AIDL,关于这些内容,读者可以参看我的博客:点击打开链接。所以本博客将以AIDL为例详细讲解Binder的IPC原理。

首先创建一个AIDL的java包,在该包中创建三个文件Book.java,Book.aidl和IBookManager.aidl代码如下:

public class Book implements Parcelable {  //Book.java

    public int bookId;
public String bookName; public Book() { } public Book(int bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
} public int describeContents() {
return 0;
} public void writeToParcel(Parcel out, int flags) {
out.writeInt(bookId);
out.writeString(bookName);
} public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
public Book createFromParcel(Parcel in) {
return new Book(in);
} public Book[] newArray(int size) {
return new Book[size];
}
}; private Book(Parcel in) {
bookId = in.readInt();
bookName = in.readString();
} @Override
public String toString() {
return String.format("[bookId:%s, bookName:%s]", bookId, bookName);
} } parcelable Book;//Book.aidl interface IBookManager { //IBookManager.aidl
List<Book> getBookList();
void addBook(in Book book);
}

其中Book.java是一个表示图书信息的java类,它实现了Parcelable接口,Book.aidl是Book类在AIDL中的声明,IBookManager.aidl是我们定义的一个接口,注意尽管Book类与IBookManager类位于同一个包中,但是在IBookManager.aidl文件中仍需要导入Book类,这是aidl文件与普通java的class文件的不同之处。

当我们定义好上述文件后Eclipse会自动为我们在工程的gen目录下生成一个与IBookManager.aidl文件相对应的java类IBookManager.java,我们来看一下其源码:

public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.test.aidl.IBookManager
{
private static final java.lang.String DESCRIPTOR = "com.test.aidl.IBookManager";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.test.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.test.aidl.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.test.aidl.IBookManager))) {
return ((com.test.aidl.IBookManager)iin);
}
return new com.test.aidl.IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder()
{
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List<com.test.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(DESCRIPTOR);
com.test.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.test.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
} private static class Proxy implements com.test.aidl.IBookManager
{
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote)
{
mRemote = remote;
} @Override
public android.os.IBinder asBinder()
{
return mRemote;
} public java.lang.String getInterfaceDescriptor()
{
return DESCRIPTOR;
} @Override
public java.util.List<com.test.aidl.Book> getBookList() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.test.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.test.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
} @Override
public void addBook(com.test.aidl.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); public java.util.List<com.test.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.test.aidl.Book book) throws android.os.RemoteException; }

可以看到系统为我们自动创建的IBookManager文件仍然是一个接口,它继承自android.os.IInterface。在其内部创建了一个抽象的静态类Stub,它继承自Binder类同时实现了IBookManager接口(但未实现该接口中的方法,因为该接口中的方法是我们在创建一个Service时在其内部创建Binder类时实现的,因此该Stub仍然属于一个抽象类)。

我们首先来梳理一下IBookManager接口的内容,可以看到,首先包含两个方法的声明getBookList和addBook,显然这两个方法是我们在IBookManager.aidl文件中声明的两个方法,另外它还定义了两个整型的id用于标识这两个方法,这两个id在其内部代理类Proxy的方法的transact过程中用于判断客户端请求的是哪一个方法。另外还包含一个内部类Stub,该Stub是一个Binder类,在这个Stub类中定义了一个内部类Proxy。

下面介绍一下上述代码中的一些重要方法:

public static com.test.aidl.IBookManager asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.test.aidl.IBookManager))) {//此时表示客户端与服务端位于同一个进程中,直接返回IInterface对象
return ((com.test.aidl.IBookManager)iin);
}
return new com.test.aidl.IBookManager.Stub.Proxy(obj);<span style="font-family: Arial, Helvetica, sans-serif;">//此时表示客户端与服务端不在同一个进程中,此时返回代理类Proxy对象</span> }

asInterface正是我们在bindServide的组件中使用ServiceConnection时首先要使用的一个方法,可以看到在方法中首先通过obj.queryLocalInterface(DESCRIPTOR)根据传入的参数获取IInterface对象,然后判断该对象是否为IBookManager类,若是则直接返回该对象,此时表示客户端与服务端位于同一个进程中,即绑定的是本地服务,即在ServiceConnection的onServiceConnected中我们是这么调用的Binder对象

public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = (MyService.MyBinder) service; //绑定的是本地服务
myBinder.doSomething();
}

否则返回IBookManager类的内部代理对象IBookManager.Stub.Proxy(obj),也就意味着客户端与服务端位于不同的进程中,此时客户端调用的方法将会是Proxy代理类中定义的方法,而不是直接调用的在Service中实现的IBookManager.aidl文件中的方法。即绑定的是远程服务,即在ServiceConnection的onServiceConnected中我们是这么调用的Binder对象

public void onServiceConnected(ComponentName name, IBinder service) {
myBinder = MyAIDL.Stub.asInterface(service) //绑定的是远程服务
myBinder.doSomething();
}

通过上面的分析可知,当绑定的是远程服务时,当在客户端调用addBook将会调用代理类中的 addBook方法,因此我们接下来看一下Proxy#addBook

@Override
public void addBook(com.test.aidl.Book book) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book!=null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
}
else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}

可以看到在该方法中首先创建了一个输入型参数Parce对象_data与输出型Parcel对象_reply,然后向该输入型参数Parce对象_data中写入一些数据,然后调用了mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);语句,当执行该语句时,即表示客户端发起RPC(远程过程调用)请求,此时当前线程会被挂起,直至RPC过程返回后,当前线程继续执行。注意该mRemote对象即为在创建该Proxy对象时传入的Binder对象,即mRemote是一个Binder对象,我们看一下Binder中的transact函数,代码如下:

 /**
* Default implementation rewinds the parcels and calls onTransact. On
* the remote side, transact calls into the binder to do the IPC.
*/
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;
}

可以看到transact函数被final修饰,即该函数是不允许被继承重写的,在该函数中最重要的一个语句: boolean r = onTransact(code, data, reply, flags);即在transact函数中调用了onTransact函数,而onTransact函数一般是要求被重写的,而重写的过程是根据我们自己定义的aidl文件IDE自动帮我们完成的,如上面所示的根据IBookManager.aidl文件自动生成IBookManager.java文件,在这个IBookManager.java文件中onTransact函数被重写。

这个onTransact函数是Binder机制运行的关键,该函数运行在服务端的Binder线程池中,当客户端发起跨进程请求时,即调用transact函数时,远程请求的数据会通过系统底层交给该函数来执行,该函数的完整签名为: public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags),服务端通过code可以确定客户端调用的是那个方法,然后从data中取出目标方法所带的参数(如果目标方法带参数的话),然后执行目标方法,当目标方法执行完毕后,就向reply中写入返回值(如果目标方法存在返回值的话),需要注意的是如果该函数返回false,则客户端的请求会失败,利用该特性可以做权限验证,因为我们也不希望任意一个进程都可以随意访问我们的服务。

通过上述的分析相信读者对Binder的运行机制的整个过程已经非常熟悉了,至于系统底层的Binder是如何传输数据的这个不重要,也不需要我们去理解,我们只需要知道Binder的运行机制的整个过程,下面用图示的方式来讲上述过程表示出来。注意下面图示仅仅表示绑定远程服务的过程,因为绑定本地服务不涉及IPC机制。如下:

对上述图示还是稍作解释,首先在客户端Client的ServiceConnection的onServiceConnected(ComponentName name, IBinder service) 方法中我们是通过   myBinder = MyAIDL.Stub.asInterface(service)语句将service转换为Binder对象,即调用的是asInterface方法而不是使用(MyService.MyBinder) service的类型转换的方式,则表示我们要获取的是远程服务而不是本地服务,那么接下来 myBinder调用的addBook(book);这个方法将会在其内部调用Binder的transact方法用来发起RPC请求,此时客户端Client被挂起,Binder的transact方法是一个final方法,不允许被修改,在该方法中调用了onTransact方法,该方法运行在服务端的Binder线程池中,该方法是整个Binder机制运行的关键,在该方法中完成IPC数据的读取与写入,该方法返回值将写入_reply参数中,然后RPC过程结束,返回数据给客户端,客户端Client被唤醒。

另外因为服务端的Binder方法是运行在Binder线程池中的,所以Binder中的方法不管是否耗时都应该采用同步的方式去实现,因为它已经运行在一个线程中。

安卓IPC机制之Binder详解的更多相关文章

  1. [置顶] 深入理解android之IPC机制与Binder框架

    [android之IPC机制与Binder框架] [Binder框架.Parcel.Proxy-Stub以及AIDL] Abstract [每个平台都会有自己一套跨进程的IPC机制,让不同进程里的两个 ...

  2. 大数据学习笔记——Spark工作机制以及API详解

    Spark工作机制以及API详解 本篇文章将会承接上篇关于如何部署Spark分布式集群的博客,会先对RDD编程中常见的API进行一个整理,接着再结合源代码以及注释详细地解读spark的作业提交流程,调 ...

  3. Android多线程----异步消息处理机制之Handler详解

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  4. android Handler机制之ThreadLocal详解

    概述 我们在谈Handler机制的时候,其实也就是谈Handler.Message.Looper.MessageQueue之间的关系,对于其工作原理我们不做详解(Handler机制详解). Messa ...

  5. 深入理解Android IPC机制之Binder机制

    Binder是Android系统进程间通信(IPC)方式之一.Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe).信号(Sign ...

  6. [安卓基础] 009.组件Activity详解

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  7. Hadoop框架:HDFS读写机制与API详解

    本文源码:GitHub·点这里 || GitEE·点这里 一.读写机制 1.数据写入 客户端访问NameNode请求上传文件: NameNode检查目标文件和目录是否已经存在: NameNode响应客 ...

  8. Linux进程间通信:IPC对象——信号灯集详解

    作者:倪老师,华清远见嵌入式学院讲师. 一.信号灯概述 信号灯与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制.相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时 ...

  9. PHP新的垃圾回收机制:Zend GC详解

    概述 在5.2及更早版本的PHP中,没有专门的垃圾回收器GC(Garbage Collection),引擎在判断一个变量空间是否能够被释放的时候是依据这个变量的zval的refcount的值,如果re ...

随机推荐

  1. [3.24校内训练赛by hzwer]

    来自FallDream的博客,未经允许,请勿转载,谢谢. ----------------------------------------------------------------------- ...

  2. SpringCloud学习之sleuth&zipkin

    一.调用链跟踪的必要性 首先我们简单来看一下下单到支付的过程,别的不多说,在业务复杂的时候往往服务会一层接一层的调用,当某一服务环节出现响应缓慢时会影响整个服务的响应速度,由于业务调用层次很“深”,那 ...

  3. Java后缀数组-求sa数组

    后缀数组的一些基本概念请自行百度,简单来说后缀数组就是一个字符串所有后缀大小排序后的一个集合,然后我们根据后缀数组的一些性质就可以实现各种需求. public class MySuffixArrayT ...

  4. Hinton's paper Dynamic Routing Between Capsules 的 Tensorflow , Keras ,Pytorch实现

    Tensorflow 实现 A Tensorflow implementation of CapsNet(Capsules Net) in Hinton's paper Dynamic Routing ...

  5. Debugging TensorFlow models 调试 TensorFlow 模型

    Debugging TensorFlow models Symbolic nature of TensorFlow makes it relatively more difficult to debu ...

  6. 【精解】EOS智能合约演练

    EOS,智能合约,abi,wasm,cleos,eosiocpp,开发调试,钱包,账户,签名权限 热身 本文旨在针对EOS智能合约进行一个完整的实操演练,过程中深入熟悉掌握整个EOS智能合约的流程,过 ...

  7. 四柱加强版汉诺塔HanoiTower----是甜蜜还是烦恼

    我想很多人第一次学习递归的时候,老师或者书本上可能会举汉诺塔的例子. 但是今天,我们讨论的重点不是简单的汉诺塔算法,而是三柱汉诺塔的延伸.先来看看经典的三柱汉诺塔. 一.三柱汉诺塔(Hanoi_Thr ...

  8. 64. Minimum Path Sum(中等, 又做出一个DP题, 你们非问我开不开心,当然开心喽!^^)

    Given an m x n grid filled with nonnegative numbers, find a path from top left to bottom right which ...

  9. Ubuntu 下安装 matlab2018a

    如果存在依赖关系无法安装,可以尝试命令:sudo apt --fix-broken install 不指明软件包而解决此问题. 参考资料:Ubuntu 16.04LTS 安装 MATLAB 2014B ...

  10. 110个oracle常用函数总结

    . ASCII 返回与指定的字符对应的十进制数; SQL) zero,ascii( ) space from dual; A A ZERO SPACE --------- --------- ---- ...