android IPC機制(二)
Binder物件
是android中的一個類,實現了IBinder介面,是一種android中實現跨程序通訊的一種方式,可以ManagerService連線各種Manager(LayoutManager,ServiceManager,ActivityManager,SensorManager等等)和各種相應ManagerService的橋樑。
AIDL檔案之自動生成的java檔案
每次書寫aidl檔案之後,系統會自動生成相應的java檔案,比如本人自己寫的一個aidl檔案:
// IBookManager.aidl
package micro.com.chapter2.inter.aidl;
// Declare any non-default types here with import statements
import micro.com.chapter2.inter.aidl.Book ;
import micro.com.chapter2.inter.aidl.Person ;
import micro.com.chapter2.inter.aidl.INewBookArriveListener ;
interface IBookManager {
List<Book> getBooks();
void addBook(in Book book) ;
void addPerson(in Person p);
}
系統自動生成的IBookManager.java檔案是:
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package micro.com.chapter2.inter.aidl;
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements micro. com.chapter2.inter.aidl.IBookManager {
private static final java.lang.String DESCRIPTOR = "micro.com.chapter2.inter.aidl.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an micro.com.chapter2.inter.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static micro.com.chapter2.inter.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof micro.com.chapter2.inter.aidl.IBookManager))) {
return ((micro.com.chapter2.inter.aidl.IBookManager) iin);
}
return new micro.com.chapter2.inter.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_getBooks: {
data.enforceInterface(DESCRIPTOR);
java.util.List<micro.com.chapter2.inter.aidl.Book> _result = this.getBooks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
micro.com.chapter2.inter.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = micro.com.chapter2.inter.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_addPerson: {
data.enforceInterface(DESCRIPTOR);
micro.com.chapter2.inter.aidl.Person _arg0;
if ((0 != data.readInt())) {
_arg0 = micro.com.chapter2.inter.aidl.Person.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addPerson(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements micro.com.chapter2.inter.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<micro.com.chapter2.inter.aidl.Book> getBooks() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<micro.com.chapter2.inter.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBooks, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(micro.com.chapter2.inter.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(micro.com.chapter2.inter.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();
}
}
@Override
public void addPerson(micro.com.chapter2.inter.aidl.Person p) 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 ((p != null)) {
_data.writeInt(1);
p.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addPerson, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
public java.util.List<micro.com.chapter2.inter.aidl.Book> getBooks() throws android.os.RemoteException;
public void addBook(micro.com.chapter2.inter.aidl.Book book) throws android.os.RemoteException;
public void addPerson(micro.com.chapter2.inter.aidl.Person p) throws android.os.RemoteException;
}
其中重要屬性和方法說明如下:
1.DESCRIPTOR:
Binder中唯一的標識,自動生成時用當前Binder類名錶示。
static final int TRANSACTION_getBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_addPerson = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
三個宣告的整型的id用於標識在transact過程中客戶端中請求的到底是哪個方法。
2.asInterface(android.os.IBinder object)
將服務端的Binder物件轉換成客戶端所需AIDL介面型別的物件,轉換是區分程序的,客戶端和伺服器在同一程序中,返回的是服務端Stub本身,否則就返回系統封裝後的Stub.proxy物件。
3.asBinder
返回當前的Binder物件
4.onTransact
本方法執行在伺服器端中Binder執行緒池中,客戶端發起跨程序請求時,遠端請求會通過系統底層封裝後交給此方法來處理:
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
通過code確定客戶端方法的方法,然後從data中取得引數(如果存在引數的話),然後執行在服務端的目標方法,執行完成之後,向reply中寫入返回值(如果客戶端中需要返回值的話)。當然onTransact方法是有返回值的,如果返回false,那麼客戶端就會請求失敗。
5.Proxy#getBookList
在客戶端中執行,當客戶端遠端呼叫此方法時,內部實現方法如下:先建立所需的輸入型Parcel物件_data,輸出型Parcel物件 _replay和返回值物件List。先將需求引數寫入_data中,接著呼叫transact方法發起遠端呼叫(RPC)請求,同時當前執行緒會掛起,然後服務端的onTransact方法會被呼叫,當RPC方法結束返回後,當前執行緒從掛起狀態變成重新執行狀態,並從_reply中取出RPC過程的返回結果,最後返回 _reply中的資料。 基本圖解如下:
linkToDeath & unlinkToDeath
Binder執行在服務端程序,如果服務程序由於某種原因異常終止,此時服務端的Binder連結斷裂(Binder死亡),會導致我們的遠端呼叫失敗。更為關鍵的是,不知道Binder連結斷裂,那麼客戶端的功能就會受影響。為了解決這個問題,Binder中提供了兩個配對的方法lineToDeath和unlinkToDeath,通過linktoDeath可以Binder設定死亡代理,Binder死亡時會受到死亡通知,此時可以重新發起連線請求從而恢復連結。
private IBinder.DeathRecipient mBinderPoolDeathRecipient = new IBinder.DeathRecipient() {
@Override
public void binderDied() {
Log.d(TAG,"binder died...");
//unlinkToDeath
xxx.unlinkToDeath(mBinderPoolDeathRecipient,0);
//restart server or rebinding ServiceConnection
}
} ;
在客戶端繫結遠端服務成功後,給binder設定死亡代理:
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
try {
//linktoDeath
xxx.linkToDeath(mBinderPoolDeathRecipient,0);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName componentName) {}
} ;
基本上通過以上兩個步驟,就會給我們的Binder設定死亡代理,當Binder死亡的時候就會受到相應的通知了。