这几天看了一下Android的IPC机制, 总感觉印象不是很深刻,所以决定写写东西加深一下印象,同时也捋一下思路。
以《Android开发艺术探索》中的例子来分析,首先新建三个文件Book.java, Book.aidl,IBookManager.aidl,代码如下:
//Book.java 代码
public class Book implements Parcelable {
int bookId;
String bookName;
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeInt(bookId);
parcel.writeString(bookName);
}
public static final Parcelable.Creator CREATOR = new Parcelable.Creator(){
@Override
public book createFromParcel(Parcel parcel) {
return new book(parcel);
}
@Override
public book[] newArray(int i) {
return new book[i];
}
};
public Book(Parcel in){
bookId = in.readInt();
bookName = in.readString();
}
public Book(int bookId,String bookName){
this.bookId = bookId;
this.bookName = bookName;
}
}
//Book.aidl 代码
package mrtang.com.ipctest; //包名
parcelable Book;
// IBookManager.aidl 代码
package mrtang.com.ipctest; //包名
import mrtang.com.ipctest.book;
interface IBookManager{
List getBookList();
void addBook(in book book);
}
点击make project,系统会自动在generated目录下生成一个IBookManager的接口,下面就开始分析这个接口:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package mrtang.com.ipctest;
public interface IBookManager extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements mrtang.com.ipctest.IBookManager
{
...
private static class Proxy implements mrtang.com.ipctest.IBookManager
{
...
}
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 getBookList() throws android.os.RemoteException;
public void addBook(mrtang.com.ipctest.book book) throws android.os.RemoteException;
}
IBookManager接口继承自IInterface接口,IBookManager主要起到提供供外部调用的接口以及将Service转换为IBinder的作用,IBookManager里面包含了一个叫Stub的内部类,他继承自Binder同时实现IBookManager。当客户端与服务端是同一个进程的时候,方法的调用不会走跨进程的transact过程,如果客户端和服务端不是同一个进程,方法的调用就会走跨进程的transact,transact的过程由Stub的内部类Proxy来实现:
/**
* 用于将服务端的Binder对象转换为客户端所需的接口类型的对象
* 在这里会实现客户端和服务端是否是同一个进程的判断
*/
public static IBookManager asInterface(IBinder obj)
{
if ((obj==null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
//obj.queryLocalInterface(String descriptor) 会通过传入的descriptor返回Binder的Owner
//Binder的Owner在Stub的构造函数中通过this.attachInterface(this, DESCRIPTOR)和descriptor绑定起来了
//所以如果iin返回的是null,则说明服务端和客户端不是同一个进程中
if (((iin!=null)&&(iin instanceof IBookManager))) {
//当客户端和服务端在同一个进程内的时候,返回Stub本身
return ((IBookManager)iin);
}
return new IBookManager.Stub.Proxy(obj);
}
当客户端和服务端不在同一个进程内的时候,返回的是Stub类的内部类Proxy对象,通过这个Proxy对象,可以实现跨进程的调用,调用的方法如下:
@Override
public java.util.List getBookList() throws android.os.RemoteException
{
//_data 用于保存要调用的函数的参数
Parcel _data = Parcel.obtain();
//_reply 用于保存要调用的函数的返回值
Parcel _reply = Parcel.obtain();
//_result 用于返回客户端调用的结果
java.util.List _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
//mRemote是客户端的Binder,他是在Stub的asInterface中return new IBookManager.Stub.Proxy(obj)中的obj
//我们通过调用他的transact方法来发送远程调用请求,与此同时当前线程挂起
//然后服务端的onTransact方法被调用,直到调用结束,当前线程继续执行,然后从_reply中取出结果
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
由于在transact的过程中当前线程会被挂起,所以我们不能在ui线程中进行太耗时的远程调用,接下来我们再回到Stub的onTransact方法:
/**
* 这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会通过封装后交由此方法进行处理
* 服务端通过code确定请求的目标方法是什么,然后从data中取出方法所需的参数,然后执行目标方法,将返回值放入reply中
* 此方法一般返回true,如果返回false,那么客户端的请求会失败,可以用这个特性做权限验证
*/
@Override
public boolean onTransact(int code,Parcel data, Parcel reply, int flags) throws RemoteException
{
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList:
{
data.enforceInterface(DESCRIPTOR);
java.util.List _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
...
}
return super.onTransact(code, data, reply, flags);
}