前段時間用AIDL模板寫了一個簡單的進程間通信demo,並且也成功完成了進程間通信,今天就順着demo來看看模板文件做了些什麼事兒。
Service
回顧demo,用了兩個app來進行實驗,ServiceApp和ClientApp。
ServiceApp啓動了一個BookService,ClientApp綁定Bookservice,進而進行進程間通信。在onServiceConnected回調中,可以拿到Ibinder,通過asInterface就可以獲取到ServiceApp中我們想要的東西,在代碼中如下:
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
bookManager = IBookManager.Stub.asInterface(service);
}
這樣一來,就轉入到AIDL文件生成的模板類中了,來看看asInterface方法做了什麼:
public static com.example.myapplication.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.example.myapplication.aidl.IBookManager))) {
return ((com.example.myapplication.aidl.IBookManager)iin);
}
return new com.example.myapplication.aidl.IBookManager.Stub.Proxy(obj);
}
首先根據DESCRIPTOR查詢進行本地,如果本地有就返回本地實現類,否則返回Proxy類。這裏返回本地實現類的話,說明客戶端和服務端在同一個進程,不需要通過進程間通信來訪問數據。在ClientApp中顯然是沒有的,所以返回了Proxy類。
private static class Proxy implements com.example.myapplication.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.example.myapplication.aidl.Book> getAllBooks() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.myapplication.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getAllBooks, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.myapplication.aidl.Book.CREATOR);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public void addBook(com.example.myapplication.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();
}
}
}
Proxy 實現了IBookManager 接口,先將需要傳輸的對象通過Parcel序列化,再調用mRemote的transact方法。通過Proxy的構造方法可以知道,這裏的mRemote就是onServiceConnected中的IBinder,也可以理解爲是ServiceApp中的BookService(不等價,先這麼理解)。
transact方法的四個參數分別是指令,入參的序列化內容,出參的序列化內容,flag。其中服務端通過指令值來判斷客戶端調用的是哪個方法,所以每個方法對應的指令不能相同。第四個參數flag的值是否等待的意思,如果是FLAG_ONEWAY值,則transact方法立馬返回,不會產生出參數據;如果是0,則會等待服務端處理完,並生成出參的序列化內容返回。可以理解爲FLAG_ONEWAY是異步,0是同步。
回到Client的onServiceConnected方法,這個方法會得到一個IBinder對象;與之對應的是Service的onBind方法,onBind方法會返回一個IBinder對象。所以來看看BookService中返回的IBinder是什麼。
private IBookManager.Stub manager = new IBookManager.Stub(){
@Override
public List<Book> getAllBooks() throws RemoteException {
return books;
}
@Override
public void addBook(Book book) throws RemoteException {
books.add(book);
}
};
@Nullable
@Override
public IBinder onBind(Intent intent) {
return manager;
}
可以看到,onBind中返回的就是IBookManager.Stub。所以服務端也轉入到AIDL文件生成的模板類中了。
public static abstract class Stub extends android.os.Binder implements com.example.myapplication.aidl.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.example.myapplication.aidl.IBookManager";
/** 省略一些代碼. */
public Stub(){
this.attachInterface(this, DESCRIPTOR);
}
@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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getAllBooks:
{
data.enforceInterface(descriptor);
java.util.List<com.example.myapplication.aidl.Book> _result = this.getAllBooks();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook:
{
data.enforceInterface(descriptor);
com.example.myapplication.aidl.Book _arg0;
if ((0!=data.readInt())) {
_arg0 = com.example.myapplication.aidl.Book.CREATOR.createFromParcel(data);
}
else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
static final int TRANSACTION_getAllBooks = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
很明顯能看到onTransact的寫法和Proxy的transact很相似,而且連參數都是一樣的。根據transact中的指令執行對應的操作,但是是將數據反序列化,跳轉到具體的實現類(即Service中的manager)中,然後再將返回值序列化,完成一次操作。
本例中,如果client和server在同一個進程時,onServiceConnected中asInterface得到的就是manager,方法調用就是普通的方法調用。
至此,ClientApp和ServiceApp中對AIDL模板類使用的全過程就分析完了,當然只分析了模板類,還沒有涉及到父類的操作,但是通過這個也能比較清晰的知道一次進程間通信在java類中的調用關係。
由此也可以看到Stub和Proxy的固有格式:Stub是IBinder的子類,並實現了IInterface;Proxy實現了IInterface,並持有了IBinder。客戶端和服務端數據傳遞時都是通過IBinder進行操作。
這是進程間通信的二小步,也是我學習binder的二大步。