Android提供了多种IPC机制,比如文件共享,使用Messenger,也可以使用ContentProvider、Socket和AIDL,基于AIDL服务的IPC,就要用到Binder。要了解Binder,不仅要熟悉它在java层的框架,还要设计其在native层的实现,过程十分复杂。
说到Binder,主要设计到4个部分,Binder驱动,ServiceManager,服务端和客户端。
(1)binder驱动:binder的核心,实现binder的底层操作。
(2)SerciceManager:提供Binder的名称到引用对象的转换服务。有点类似于DNS。
(3)服务端:Binder服务的提供者,也就是binder实体对象
(4)客户端:得到服务端binder实体对象的引用对象,并调用服务端的方法。
先从最简单的 ServiceManager说起,为什么说它类似于DNS呢,因为每一个Binder服务,都有一个唯一的字符串标识,只要这个Binder服务向ServiceManager注册了,那么客户就可以通过这个字符串标识查询到这个Binder服务的引用对象。但是ServiceManager是一个单独的进程,对于其他进程的请求查询,它相当于一个Service,其他的进程是怎么找到ServiceManager的呢?其实,ServiceManager比较特殊,它不用注册,它向Binder驱动发送BINDER_SET_CONTEXT_MGR命令,binder驱动会创建ServiceManager对象实体,并在binder驱动硬性的规定了0代表ServiceManager,这样,其他的进程就可以通过binder驱动的0直接得到ServiceManager的引用对象。这样,binder服务就可以在ServiceManager中注册了,客户进程就可以通过在ServiceManager查询得到这个Binder服务的引用对像了。
但是不是所有的Binder服务是可以在ServiceManager中注册的,只有root进程或者System进程才可以在ServiceManager注册服务,如ActivityManagerService。这类服务称为Biner实名服务。普通应用开发的binder服务只能是匿名服务。匿名服务无法在ServiceManager注册,也就无法通过ServiceManager查询到,那么匿名服务怎么IPC通信呢,其实也是通过BInder,在JAVA层Android提供了组件Service的方式来包装和使用匿名服务。当我们在客户端通过bindService()发送一个Intent时候,根据该Intent启动一个Service,在组件Service中的binder对象随之启动,然后这个biner对象通过onServiceConnected()传回到客户端,这样客户端得到组件service的binde引用对象了,得到这个引用对象后,客户端就可以向在本地进程中调用方法一样调用组件service中的方法了。(注:不要将组件service和binder服务端弄混淆了,在这里只是借用组件service来实现binder的匿名服务)
当我们使用Intent和Binder来进行传输数据时,就要把数据进行序列化,序列化在android平台上提供了两种方法,一种是继承Serializable接口I,一种继承Parcelable接口。Serializable是java的序列化接口,使用起来简单但是开销很大,序列化和反序列化过程需要大量的I/O操作,Parcelable是Android中的序列化方式,使用起来麻烦但是效率很高,更适合于Android平台上。在java开发进程间通信中,应用binder的主要是AIDL,AIDL是android提供的一个跨进程通信的方式,它主要利用binder机制,下面提供一个AIDL的例子来创建一个binder匿名服务并和客户端进行跨进程通信。
这个例子就是在一个应用A中创建一个组件service,这个service实现两个方法,addbook()和getBookList()。然后另外一个应用B中调用这两个方法,应用B充当客户端。这里两个应用要传递book这个对象,所以book是进行序列化和反序列化,这里我给出序列化代码。
public class Book implements Parcelable{
public int bookId;
public String bookName;
public Book(int bookId,String bookName){
this.bookId = bookId;
this.bookName = bookName;
}
@Override
public int describeContents() {
// TODO Auto-generated method stub
return 0;
}
@Override
public void writeToParcel(Parcel out, int arg1) {
// TODO Auto-generated method stub
out.writeInt(bookId);
out.writeString(bookName);
}
public static final Parcelable.Creator<Book> CREATOR = new Parcelable.Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
// TODO Auto-generated method stub
return new Book(in);
}
@Override
public Book[] newArray(int size) {
// TODO Auto-generated method stub
return new Book[size];
}
};
private Book(Parcel in){
bookId= in.readInt();
bookName = in.readString();
}
}
接下来我们要申明
AIDL
接口,在接口中申明
service
要实现的两个方法
,eclipse
会自动把
AIDL
文件转化为
java
文件。新建
Java
包,里面有
3
个文件,
Book.java,Book.aidl
和
IBookManager.aidl,Book.java文件代码上面已经给出。下面,我给出Book.aidl和IBooManager.aidl代码。
book.aidl如下:
package com.ryg.chapter_2.aidl;
parcelable Book;
因为book对象继承Parcelable序列化的,所以要写aidl文件申明
IBookManager.aidl如下:
package com.ryg.chapter_2.aidl;
import com.ryg.chapter_2.aidl.Book;
interface IBookManager{
List<Book> getBookList();
void addBook(in Book book);
}
这时候你看project下面的gen文件,它自动把这两个aidl文件转化成了java文件。我们也可以自己写出这些java文件的,AIDL让我们只要申明接口就自己帮我们转换了,为
我们节省了不少事。
写完了这几个文件后,我们就可以实现组件Service了。
远程服务端实现:
public class BookManagerService extends Service{
private static final String TAG = "BMS";
private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();
private Binder mBinder = new IBookManager.Stub() {
@Override
public List<Book> getBookList() throws RemoteException {
// TODO Auto-generated method stub
return mBookList;
}
@Override
public void addBook(Book book) throws RemoteException {
// TODO Auto-generated method stub
mBookList.add(book);
}
};
@Override
public IBinder onBind(Intent arg0) {
// TODO Auto-generated method stub
return mBinder;
}
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
mBookList.add(new Book(1,"Android"));
mBookList.add(new Book(2,"Ios"));
}
}
至此,应用A即远程服务端的代码实现了。
接下来,我们应用B即客户端的实现。
将应用A中的book.java,book.aidl,IBookManager.aidl的3个文件拷贝到应用B的工程文件下,放在一个包中,且这个包的名字和应用A中放这3个
文件的包名字要一致(不要将这包名同时作为这两个应用的启动包名,不然会冲突)。
客户端实现比较简单了,代码如下:
public class BookManagerActivity extends Activity implements OnClickListener{
private static final String TAG = "BookManagerActivity";
private ServiceConnection mConnection = new ServiceConnection(){
@Override
public void onServiceConnected(ComponentName arg0, IBinder service) {
// TODO Auto-generated method stub
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try{
list = bookManager.getBookList();
Book newBook = new Book(3,"android开发");
Log.i(TAG,"query book list,list type:"+list.getClass().
getCanonicalName());
Log.i(TAG,"query book list:"+list.toString());
} catch (RemoteException){
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName arg0) {
// TODO Auto-generated method stub
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent("org.yi.Action");
bindService(intent,mConnection,Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
unbindService(mConnection);
super.onDestroy();
}
}
注意,这里组件service的acrtion配置成org.yi.Action.
整个AIDL流程讲完了,下一篇将对AIDL文件自动生成的java文件和binderq驱动进行分析,