在上一篇文章中Android中AIDL的使用詳解介紹了AIDL的使用流程,這篇文章我們說說AIDL的工作原理。
IPC
在這之前我們先簡單說一下IPC,IPC是Inter-Process Communication的縮寫,是進程間通信或者跨進程通信的意思,既然說到進程,大家要區分一下進程和線程,進程一般指的是一個執行單元,它擁有獨立的地址空間,也就是一個應用或者一個程序。線程是CPU調度的最小單元,是進程中的一個執行部分或者說是執行體,兩者之間是包含與被包含的關系。因為進程間的資源不能共享的,所以每個系統都有自己的IPC機制,Android是基于Linux內核的移動操作系統,但它并沒有繼承Linux的IPC機制,而是有著自己的一套IPC機制。
Binder
Binder就是Android中最具特色的IPC方式,AIDL其實就是通過Binder實現的,因為在我們定義好aidl文件后,studio就幫我們生成了相關的Binder類。事實上我們在使用AIDL時候繼承的Stub類,就是studio幫我們生成的Binder類,所以我們可以通過查看studio生成的代碼來了解Binder的工作原理。首先我們定義一個AIDL文件
// UserManager.aidl
package cc.abto.demo;
interface UserManager {
String getName();
String getOtherName();
}
sycn project工程后,查看生成的UserManager.java文件
package cc.abto.demo;
// Declare any non-default types here with import statements
public interface UserManager extends android.os.IInterface
{
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements cc.abto.demo.UserManager
{
//...
}
public java.lang.String getName() throws android.os.RemoteException;
public java.lang.String getOtherName() throws android.os.RemoteException;
}
生成的代碼還是比較多的,我就不一次性全部貼上來了,先按類結構來看。
sutido幫我們生成了一個繼承android.os.IInterface接口的UserManager接口,所有在Binder中傳輸的接口都必須實現IInterface接口。接口定義了我們在AIDL文件中定義的方法,然后還有個內部靜態類Stub,我們接著看這個Stub。
public static abstract class Stub extends android.os.Binder implements cc.abto.demo.UserManager
{
private static final java.lang.String DESCRIPTOR = "cc.abto.demo.UserManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an cc.abto.demo.UserManager interface,
* generating a proxy if needed.
*/
public static cc.abto.demo.UserManager asInterface(android.os.IBinder obj)
{
if ((obj == null))
{
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof cc.abto.demo.UserManager)))
{
return ((cc.abto.demo.UserManager) iin);
}
return new cc.abto.demo.UserManager.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_getName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
case TRANSACTION_getOtherName:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _result = this.getOtherName();
reply.writeNoException();
reply.writeString(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements cc.abto.demo.UserManager
{
//...
}
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getOtherName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
Stub繼承了android.os.Binder并實現UserManager接口,下圖是Stub的類結構。
我們可以看到Stub中的常量,其中兩個int常量是用來標識我們在接口中定義的方法的,DESCRIPTOR常量是 Binder的唯一標識。
asInterface 方法用于將服務端的Binder對象轉換為客戶端所需要的接口對象,該過程區分進程,如果進程一樣,就返回服務端Stub對象本身,否則呢就返回封裝后的Stub.Proxy對象。
onTransact 方法是運行在服務端的Binder線程中的,當客戶端發起遠程請求后,在底層封裝后會交由此方法來處理。通過code來區分客戶端請求的方法,注意一點的是,如果該方法返回false的換,客戶端的請求就會失敗。一般可以用來做權限控制。
最后我們來看一下Proxy代理類。
private static class Proxy implements cc.abto.demo.UserManager
{
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.lang.String getName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try
{
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally
{
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public java.lang.String getOtherName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try
{
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getOtherName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally
{
_reply.recycle();
_data.recycle();
}
return _result;
}
}
代理類中我們主要看一下getName和getOtherName方法就可以了,這兩個方法都是運行在客戶端,當客戶端發起遠程請求時,_data會寫入參數,當然這邊的例子并沒有(啦啦啦...),然后調用transact方法發起RPC(遠程過程調用)請求,同時掛起當前線程,然后服務端的onTransact方法就會被 調起,直到RPC過程返回后,當前線程繼續執行,并從_reply取出返回值(如果有的話),并返回結果。
最后
分析完sutido生成的Binder之后,我們就大概知道AIDL的工作原理,定義好AIDL文件只是方便sutido幫我生成所需的Binder類,AIDL并不是必須的文件,因為這個Binder類我們也可以手寫出來(當然,你閑的沒事的話),所以這邊最重要的還是Binder的知識點,其他一些IPC方式其實都是通過Binder來實現的,比如說Messager,Bundle,ContentProvider,只是它們的封裝方式不一樣而已。總的來說,從應用層來說,Binder是客戶端和服務端之間通信的媒介。從FrameWork層來說,Binder是ServiceManager連接各種Manager和ManagerService的橋梁。Android系統中充斥著大量的CS模型,而Binder作為獨有的IPC方式,如果我們能更好的理解它,對我們的開發工作就會帶來更多的幫助。