Android的Binder是一种跨进程的通信机制,和TCP机制很像,其实也可以抽象的进行分层,可以把Binder想象成一种跨进程通信的协议。
学习Binder机制最好是以aidl为例,因为sdk会帮助我们将aidl文件生成利用Binder机制通信的代码,所以本篇以aidl为例来记录一下Binder机制的学习过程。
在学习之前,和Binder机制相关的几个类需要介绍一下,主要有Binder, IBinder, IInterface,Binder是IBinder的实现类,IInterface是与Binder相对应的,下面通过源码的注释,简单的看一下,其实源码的注释都很详细,通过看注释,可以知道类的大概功能。
首先看下Binder,public class Binder implements IBinder
,Binder继承IBinder接口,Binder类的注释如下:
/**
* Base class for a remotable object, the core part of a lightweight
* remote procedure call mechanism defined by {@link IBinder}.
* This class is an implementation of IBinder that provides
* standard local implementation of such an object.
*
* <p>Most developers will not implement this class directly, instead using the
* <a href="{@docRoot}guide/components/aidl.html">aidl</a> tool to describe the desired
* interface, having it generate the appropriate Binder subclass. You can,
* however, derive directly from Binder to implement your own custom RPC
* protocol or simply instantiate a raw Binder object directly to use as a
* token that can be shared across processes.
*
* <p>This class is just a basic IPC primitive; it has no impact on an application's
* lifecycle, and is valid only as long as the process that created it continues to run.
* To use this correctly, you must be doing so within the context of a top-level
* application component (a {@link android.app.Service}, {@link android.app.Activity},
* or {@link android.content.ContentProvider}) that lets the system know your process
* should remain running.</p>
*
* <p>You must keep in mind the situations in which your process
* could go away, and thus require that you later re-create a new Binder and re-attach
* it when the process starts again. For example, if you are using this within an
* {@link android.app.Activity}, your activity's process may be killed any time the
* activity is not started; if the activity is later re-created you will need to
* create a new Binder and hand it back to the correct place again; you need to be
* aware that your process may be started for another reason (for example to receive
* a broadcast) that will not involve re-creating the activity and thus run its code
* to create a new Binder.</p>
可以看到Binder主要用于跨进程的通信,是IBinder接口的本地实现。
那么在看下IBinder接口:public interface IBinder
/**
* Base interface for a remotable object, the core part of a lightweight
* remote procedure call mechanism designed for high performance when
* performing in-process and cross-process calls. This
* interface describes the abstract protocol for interacting with a
* remotable object. Do not implement this interface directly, instead
* extend from {@link Binder}.
*
* <p>The key IBinder API is {@link #transact transact()} matched by
* {@link Binder#onTransact Binder.onTransact()}. These
* methods allow you to send a call to an IBinder object and receive a
* call coming in to a Binder object, respectively. This transaction API
* is synchronous, such that a call to {@link #transact transact()} does not
* return until the target has returned from
* {@link Binder#onTransact Binder.onTransact()}; this is the
* expected behavior when calling an object that exists in the local
* process, and the underlying inter-process communication (IPC) mechanism
* ensures that these same semantics apply when going across processes.
*
* <p>The data sent through transact() is a {@link Parcel}, a generic buffer
* of data that also maintains some meta-data about its contents. The meta
* data is used to manage IBinder object references in the buffer, so that those
* references can be maintained as the buffer moves across processes. This
* mechanism ensures that when an IBinder is written into a Parcel and sent to
* another process, if that other process sends a reference to that same IBinder
* back to the original process, then the original process will receive the
* same IBinder object back. These semantics allow IBinder/Binder objects to
* be used as a unique identity (to serve as a token or for other purposes)
* that can be managed across processes.
*
* <p>The system maintains a pool of transaction threads in each process that
* it runs in. These threads are used to dispatch all
* IPCs coming in from other processes. For example, when an IPC is made from
* process A to process B, the calling thread in A blocks in transact() as
* it sends the transaction to process B. The next available pool thread in
* B receives the incoming transaction, calls Binder.onTransact() on the target
* object, and replies with the result Parcel. Upon receiving its result, the
* thread in process A returns to allow its execution to continue. In effect,
* other processes appear to use as additional threads that you did not create
* executing in your own process.
*
* <p>The Binder system also supports recursion across processes. For example
* if process A performs a transaction to process B, and process B while
* handling that transaction calls transact() on an IBinder that is implemented
* in A, then the thread in A that is currently waiting for the original
* transaction to finish will take care of calling Binder.onTransact() on the
* object being called by B. This ensures that the recursion semantics when
* calling remote binder object are the same as when calling local objects.
*
* <p>When working with remote objects, you often want to find out when they
* are no longer valid. There are three ways this can be determined:
* <ul>
* <li> The {@link #transact transact()} method will throw a
* {@link RemoteException} exception if you try to call it on an IBinder
* whose process no longer exists.
* <li> The {@link #pingBinder()} method can be called, and will return false
* if the remote process no longer exists.
* <li> The {@link #linkToDeath linkToDeath()} method can be used to register
* a {@link DeathRecipient} with the IBinder, which will be called when its
* containing process goes away.
* </ul>
*
* @see Binder
*/
IBinder中的注释包含了很多有用的信息:
1.IBinder 接口描述了呵远程对象交互的一个抽象协议
2.IBinder接口中有个很重要的接口transact接口,这个api是和Binder类中的onTransact方法相对应的。同时transact接口是同步的,这就解释了,当用aidl调用远程接口时,如果远程接口做的是耗时操作,那么需要写在子线程的原因了。
3.注释中的第三段说的意思是,transact接口中跨进程传递的数据是Parcel类型的,Parcel类型感觉上是主要为Binder通信设计的,Parcel是一个容器,这个容器不仅能包含内容,还能包含对象的引用。下面看Parcel中的一段注释
<h3>Active Objects</h3>
* <p>An unusual feature of Parcel is the ability to read and write active
* objects. For these objects the actual contents of the object is not
* written, rather a special token referencing the object is written. When
* reading the object back from the Parcel, you do not get a new instance of
* the object, but rather a handle that operates on the exact same object that
* was originally written. There are two forms of active objects available.</p>
*
* <p>{@link Binder} objects are a core facility of Android's general cross-process
* communication system. The {@link IBinder} interface describes an abstract
* protocol with a Binder object. Any such interface can be written in to
* a Parcel, and upon reading you will receive either the original object
* implementing that interface or a special proxy implementation
* that communicates calls back to the original object. The methods to use are
* {@link #writeStrongBinder(IBinder)},
* {@link #writeStrongInterface(IInterface)}, {@link #readStrongBinder()},
* {@link #writeBinderArray(IBinder[])}, {@link #readBinderArray(IBinder[])},
* {@link #createBinderArray()},
* {@link #writeBinderList(List)}, {@link #readBinderList(List)},
* {@link #createBinderArrayList()}.</p>
*
* <p>FileDescriptor objects, representing raw Linux file descriptor identifiers,
* can be written and {@link ParcelFileDescriptor} objects returned to operate
* on the original file descriptor. The returned file descriptor is a dup
* of the original file descriptor: the object and fd is different, but
* operating on the same underlying file stream, with the same position, etc.
* The methods to use are {@link #writeFileDescriptor(FileDescriptor)},
* {@link #readFileDescriptor()}.
*
这段意思简单来说是:Parcel可以读写“活的”对象,但是Parcel中写的不是对象的内容,而是写的一个token,这个token可以标记那个对象,这样通过token就可以找到那个“活的”对象。Parcel支持两种这样的对象,一个就是咱们正在学习的Binder,一个是FileDescriptor。以Binder为例,当我们用Parcel传输Binder的时候,当读Binder的时候,我们就会判断,本地是否有这个或者的对象,如果有,说明时本地调用,则直接使用活着的对象,如果不是则实现个代理类,这个代理类来和远程的原始对象通信,这个也是Binder的机制,后面讲aidl时,再通过代码详细说下。
知道了Parcel类的用处,就基本上明白了IBinder注释中的第三段了。
4.第四段说的是,远程接口的方法是运行在Binder的线程池的。Binder有个线程池,当跨进程请求过来的时候,线程池里面的线程会启动,之后调用Binder的onTransact函数,之后onTransact调用具体的接口
5.IBinder支持跨进程递归。举例,Process A 调用Process B,这是Process B在onTransact方法中通过Binder调用Process A的接口,这时,Process A的Binder线程池回去执行相应的接口,这个Process A在本地调用是同样的效果。
6.可以通过 transact pingBinder linkToDeath等方法来判断远程的Binder是否还活着。
再说下IInterface接口,源码如下:
/**
* Base class for Binder interfaces. When defining a new interface,
* you must derive it from IInterface.
*/
public interface IInterface
{
/**
* Retrieve the Binder object associated with this interface.
* You must use this instead of a plain cast, so that proxy objects
* can return the correct result.
*/
public IBinder asBinder();
}
可以看到,IInterface接口时和Binder相对应的,如果我们想通过Binder进行跨进程的调用,那么我们的接口就应该继承IInterface接口,后面说aidl的时候也会看到。
通过上面的介绍大体了解了Binder中几个基本的类,下面通过aidl来看下Binder的运行过程。
我们知道aidl是一种接口定义语言,用于android的跨进程通信,当我们定义好aidl文件之后,android的sdk会帮我们自动生成相应的Java文件,真正实现跨进程通信功能的是生成的java文件,aidl只是一个产生java文件的工作,相当于android帮我们简化了工作,所以我们主要来分析生成的java文件。
首先我们先定义一个aidl文件,这个文件很简单,定义两个接口:
package com.example.aidl;
interface ICalculate{
int add(int a, int b);
int del(int a, int b);
}
这时,我们在gen目录下一个对应的Java文件,如下:
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: /Users/zxw/android_develop/eclipse_workspace/AIDLLearning/src/com/example/aidl/ICalculate.aidl
*/
package com.example.aidl;
public interface ICalculate extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.example.aidl.ICalculate {
private static final java.lang.String DESCRIPTOR = "com.example.aidl.ICalculate";
/** Construct the stub at attach it to the interface. */
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.aidl.ICalculate interface,
* generating a proxy if needed.
*/
public static com.example.aidl.ICalculate asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.aidl.ICalculate))) {
return ((com.example.aidl.ICalculate) iin);
}
return new com.example.aidl.ICalculate.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_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;
}
case TRANSACTION_del: {
data.enforceInterface(DESCRIPTOR);
int _arg0;
_arg0 = data.readInt();
int _arg1;
_arg1 = data.readInt();
int _result = this.del(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.aidl.ICalculate {
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 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;
}
@Override
public int del(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_del, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_del = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public int add(int a, int b) throws android.os.RemoteException;
public int del(int a, int b) throws android.os.RemoteException;
}
可以看到ICalculate接口继承了IInterface接口,根据之前的介绍可知,因为IInterface是和Binder相对应的接口,所以ICalculate就是和Binder相对应的接口。
在ICalculate接口中有个内部类Stub,这个内部类非常的重要,当客户端用bindService去bind服务的时候,server端会返回Stub实现类的实例,客户端得到binder之后,会调用asInterface静态方法,来把binder的对应IInterface接口拿到,可以看到在asInterface方法中,首先会调用Binder的queryLocalInterface方法,此方法的作用是,找到该Binder对应的接口实例,之后,判断此实例是否为本地(client端)的实例,如果是,那么就是本地调用,并没有跨进程,直接返回对应接口,如果得到的IInterface实例不是本地的对象实例,那么说明是跨进程调用的,这时会用Proxy类来进行一个包装。Proxy类继承ICalculate接口,new出对象后,将Proxy对象返回。
在跨进程调用的这种情况下,client拿到Proxy实例后,调用接口中的方法,比如add方法,在Proxy的add方法中会调用
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
之后,而transact方法内会调用onTransact方法,在onTransact的方法内,调用了与Binder对应的接口的接口方法,这样就完成了Binder的通信。
通过上面的分析可以知道,即便我们不建立aidl文件,也是可以实现Binder通信的,只需要仿照aidl生成的java文件,调用Binder的transact方法即可。
在之前提到,客户端通过bindService,利用Binder机制来进行跨进程通信,那么bindService是怎么执行的呢,下一篇文章趁热打铁,接着分析一下bindServce的机制。