Binder Java層的實現原理分析

Binder Java層實現原理

近日在作組件化方案時,複習了一遍Bidner機制,在熟悉了一遍Bidner機制後,對進程間通信以及Android設計模式原來有了較深的感悟。 Android Binder 是一個及其深刻的話題,從Linux間進程通訊的方式,到Android間通訊方式都須要瞭解,下圖是binder大體實現(看完我就暈了)。本文不經過複雜的代碼細節,以及底層代碼分析Binder實現方式。而是經過相對好理解的AIDL(也是大衆認爲的最好理解binder的方式)來熟悉binder。對AIDL不瞭解的朋友,可先行看下三步掌握Android中的AIDL來大體瞭解一下期使用方式。java

複雜的binder

Binder介紹

這裏咱們不講述上面複雜的實現。而是經過AIDL介紹binder的上層實現原理。Binder是Android中的一個類,它繼承了IBinder接口。android

  • 從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文件 代碼以下:app

Market.javaide

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.aidlui

// 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文件這個文件就是咱們研究的重中之重!它的所有代碼以下:this

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;
}
複製代碼

看起來很複雜,但其實這個類邏輯仍是蠻清晰。咱們縮進下代碼在看。 spa

首先這裏聲明瞭兩個方法, 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開始瞭解並深刻,對您的成長是很是有意義的!

相關文章
相關標籤/搜索