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開始瞭解並深入,對您的成長是非常有意義的!