Binder Java層實現原理
近日在做元件化方案時,複習了一遍Bidner機制,在熟悉了一遍Bidner機制後,對程式間通訊以及Android設計模式原來有了較深的感悟。 Android Binder 是一個及其深入的話題,從Linux間程式通訊的方式,到Android間通訊方式都需要了解,下圖是binder大致實現(看完我就暈了)。本文不通過複雜的程式碼細節,以及底層程式碼分析Binder實現方式。而是通過相對好理解的AIDL(也是大眾認為的最好理解binder的方式)來熟悉binder。對AIDL不瞭解的朋友,可先行看下三步掌握Android中的AIDL來大致瞭解一下期使用方式。
Binder介紹
這裡我們不講述上面複雜的實現。而是通過AIDL介紹binder的上層實現原理。Binder是Android中的一個類,它繼承了IBinder介面。
- 從IPC角度來說,Binder是Android中的一種跨程式通訊方式。
- Binder還可以理解為一種虛擬的物理裝置,它的裝置驅動是/dev/binder,該通訊方式在Linux中沒有
- 從Android Framework角度來說,Binder是ServiceManager連線各種 Manager(ActivityManager、WindowManager,等等)和相應ManagerService的橋樑;
- 從Android應用層來說,Binder是客戶端和服務端進行通訊的媒介,當bindService的時候,服務端會返回一個包含了服務端業務呼叫的Binder物件,通過這個Binder物件,客戶端就可以獲取服務端提供的服務或者資料,這裡的服務包括普通服務和基於AIDL的服務。
下面是通過Aidl實現程式間通訊的簡化圖,您可以不記住,這裡有個印象就好。
建立AIDL
我們先建立三個檔案分別為Market.java、 Market.aidl、ImarketManger.aidl檔案 程式碼如下:
Market.java
public class Market implements Parcelable {
private int goodsId;
private String goodsName;
public Market(int goodsId, String goodsName) {
this.goodsId = goodsId;
this.goodsName = goodsName;
}
protected Market(Parcel in) {
goodsId = in.readInt();
goodsName = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(goodsId);
dest.writeString(goodsName);
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Market> CREATOR = new Creator<Market>() {
@Override
public Market createFromParcel(Parcel in) {
return new Market(in);
}
@Override
public Market[] newArray(int size) {
return new Market[size];
}
};
}
複製程式碼
Market.aidl
// Market.aidl
package com.ccdt.itvision.demo;
parcelable Market;
複製程式碼
ImarketManger.aidl
// IMarketManger.aidl
package com.ccdt.itvision.demo;
import com.ccdt.itvision.demo.Market;
// Declare any non-default types here with import statements
interface IMarketManger {
List<Market> getGoodList();
void addGoods(in Market market);
}
複製程式碼
上面三個檔案中,Market表示一個超市食物類,它實現了Parcelable介面。Market.aidl是Market類在AIDL中的宣告。IMarketManger.aidl是我們自己定義的一個介面。裡面有兩個方法。然後我們rebuild project
會在 app/build/generated/source/com.xxx.xxx/ 看見ImarketManger.java檔案這個檔案就是我們研究的重中之重!它的全部程式碼如下:
public interface IMarketManger extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.ccdt.itvision.viewlearning.IMarketManger {
private static final java.lang.String DESCRIPTOR = "com.ccdt.itvision.demo.IMarketManger";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.ccdt.itvision.viewlearning.IMarketManger interface,
* generating a proxy if needed.
*/
public static com.ccdt.itvision.viewlearning.IMarketManger asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.ccdt.itvision.viewlearning.IMarketManger))) {
return ((com.ccdt.itvision.viewlearning.IMarketManger) iin);
}
return new com.ccdt.itvision.viewlearning.IMarketManger.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_getGoodList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.ccdt.itvision.viewlearning.Market> _result = this.getGoodList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addGoods: {
data.enforceInterface(DESCRIPTOR);
com.ccdt.itvision.viewlearning.Market _arg0;
if ((0 != data.readInt())) {
_arg0 = com.ccdt.itvision.demo.Market.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addGoods(_arg0);
reply.writeNoException();
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.ccdt.itvision.viewlearning.IMarketManger {
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.ccdt.itvision.viewlearning.Market> getGoodList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.ccdt.itvision.viewlearning.Market> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getGoodList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.ccdt.itvision.viewlearning.Market.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addGoods(com.ccdt.itvision.viewlearning.Market market) 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 ((market != null)) {
_data.writeInt(1);
market.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addGoods, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getGoodList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addGoods = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.ccdt.itvision.viewlearning.Market> getGoodList() throws android.os.RemoteException;
public void addGoods(com.ccdt.itvision.viewlearning.Market market) throws android.os.RemoteException;
}
複製程式碼
看起來很複雜,但其實這個類邏輯還是蠻清晰。我們縮排下程式碼在看。
首先這裡宣告瞭兩個方法,getGoodList()
和 addGoods()
。這兩個方法顯然是我們在ImarketManger中宣告的。然後是一個靜態的內部類Stub。stub繼承自Binder,顯然他自己就是一個Binder類。當客戶端和服務端都位於同一個程式時,方法呼叫不會走跨程式的transact過程,而當兩者位於不同程式時,方法呼叫需要走transact過程,這個邏輯由Stub的內部代理類Proxy來完成。所以這裡的核心就是Stub的內部代理Proxy。Proxy內部方法如下:
- asInterface(android.os.IBinder obj)
用於將服務端的Binder物件轉換成客戶端所需的AIDL介面型別的物件,這種轉換過程是區分程式的,如果客戶端和服務端位於同一程式,那麼此方法返回的就是服務端的Stub物件本身,否則返回的是系統封裝後的Stub.proxy物件。
- asBinder
此方法用於返回當前Binder物件
- onTransact
此方法執行在服務端中的Binder執行緒池中,當客戶端通過aidl發起跨程式請求時,遠端請求會通過系統底層封裝後交由此方法來處理。服務端通過code可以確定客戶端所請求的方法是什麼,接著從data中取出該方法所需的引數(沒引數則不取),然後執行該方法。當該方法執行完畢後,就向reply中寫入返回值,這就是onTransact的執行過程。值得注意的是,如果此方法返回false,那麼客戶端的請求會失敗
- Proxy#getGoodsList
首先建立該方法所需要的輸入型Parcel物件_data、輸出型Parcel物件_reply。 宣告返回值物件List。 _data.writeInterfaceToken(DESCRIPTOR); 將該方法所需引數資訊寫入data 中。 接著呼叫transact方法來發起RPC(遠端過程呼叫)請求,當前執行緒hang on(掛起),當服務端 onTransact(剛介紹完的方法,回頭去看一看)執行完畢後。從_reply中取出RPC過程的返回結果處理完畢。
通過以上分析,下面我們在回頭看剛剛的簡圖是不是就明朗了許多?
手寫一個AIDL的實現類
從上述分析過程來看,我們完全可以不提供AIDL檔案即可實現Binder,之所以提供AIDL檔案,是為了方便系統為我們生成程式碼。系統根據AIDL檔案生成Java檔案的格式是固定的,我們可以不用AIDL檔案直接寫一個Binder出來。
首先宣告一個介面繼承 IInterface
程式碼如下:
public interface IMarketManger extends IInterface {
static final String DESCRIPTOR = "com.ccdt.itvision.demo.custombinder.IMarketManger";
static final int TRANSACTION_getGoodList = IBinder.FIRST_CALL_TRANSACTION + 0;
static final int TRANSACTION_addGood = IBinder.FIRST_CALL_TRANSACTION+1;
public List<Market> getGoodList() throws RemoteException;
public void addGoods(Market market) throws RemoteException;
}
複製程式碼
接著,建立ImarketManger的實現類,實現思路與AIDL生成的程式碼類似。如果服務端使用手寫Binder,只需要在onBinder的時候返回ImarketManger的實現類就好。程式碼如下:
public class MarketImpl extends Binder implements IMarketManger {
public MarketImpl() {
this.attachInterface(this, DESCRIPTOR);
}
public static IMarketManger asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IInterface iInterface = obj.queryLocalInterface(DESCRIPTOR);
if (iInterface != null && iInterface instanceof IMarketManger) {
return (IMarketManger) iInterface;
}
return new MarketImpl.Proxy(obj);
}
@Override
public List<Market> getGoodList() throws RemoteException {
return null;
}
@Override
public void addGoods(Market good) throws RemoteException {
}
@Override
public IBinder asBinder() {
return this;
}
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
switch (code) {
case INTERFACE_TRANSACTION:
reply.writeString(DESCRIPTOR);
return true;
case TRANSACTION_getGoodList:
data.enforceInterface(DESCRIPTOR);
List<Market> goodList = this.getGoodList();
reply.writeNoException();
reply.writeTypedList(goodList);
return true;
default:
break;
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements IMarketManger {
private IBinder mRemote;
public Proxy(IBinder obj) {
mRemote = obj;
}
@Override
public List<Market> getGoodList() throws RemoteException {
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
List<Market> result;
try {
data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getGoodList, data, reply, 0);
reply.readException();
result = reply.createTypedArrayList(Market.CREATOR);
} finally {
reply.recycle();
data.recycle();
}
return result;
}
@Override
public void addGoods(Market market) throws RemoteException {
// TODO: 與aidl中一樣
}
@Override
public IBinder asBinder() {
return mRemote;
}
}
複製程式碼
Aidl只是Google為我們實現Binder 實現的一個簡便方式生成模板化程式碼,事實上我們完全可以這樣手寫一個aidl,這有利於我們理解Bidner的實現方式。另外Binder還有個重要的方法,Linktodeath()
因服務端的情況我們未知,可能出現異常情況,當binder 死亡時,我們可以通過binder死亡代理 知道連結已經斷開,重新繫結連結。
最後Binder的實現機制看似複雜,但是其中很有條理,如果你想使用元件化方案,瞭解android底層原理,做framwork層程式碼,binder 使我們繞不開的話題,所以先在Aidl開始瞭解並深入,對您的成長是非常有意義的!