天天看點

Android插件化基礎--Binder機制

Binder

是一套基于CS的架構。下面以一個極簡的例子來學習

Binder

1.首先定義一個

IMedia.aidl

檔案。
interface IMedia {
    boolean start();
    boolean stop();
}
           
2.然後IDE會幫我們自動生成一個

IMedia.java

檔案
public interface IMedia extends android.os.IInterface {

        public static abstract class Stub extends android.os.Binder implements com.cmeiyuan.pluginstudy.IMedia {

                public android.os.IBinder asBinder(){
                    return this;
                }

                public static com.cmeiyuan.pluginstudy.IMedia asInterface(android.os.IBinder obj) {
                    ...
                }

                public boolean onTransact(...){
                    ...
                    start();
                    ...
                }

                private static class Proxy implements com.cmeiyuan.pluginstudy.IMedia {
                    ...
                }
        }
    }
    public boolean start() throws android.os.RemoteException;
}
           

IMedia

是一個繼承于

android.os.IInterface

的接口,它的内部類

Stub

實作了

android.os.IInterface

asBinder()

方法,直接傳回了

Sub

類執行個體。我們繼續看

Sub

類的

asInterface()

方法

public static com.cmeiyuan.pluginstudy.IMedia asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof com.cmeiyuan.pluginstudy.IMedia))) {
        return ((com.cmeiyuan.pluginstudy.IMedia) iin);
    }
    return new com.cmeiyuan.pluginstudy.IMedia.Stub.Proxy(obj);
}
           

這個方法的作用是将遠端

Binder

對象轉換為友善使用的

IMedia

接口對象,這個遠端

Binder

對象是我們

bindService()

時傳回的。首選通過

obj.queryLocalInterface(DESCRIPTOR)

查詢是否有本地接口對象,這種情況是當

Server

端和

Client

端處于同一程序,沒有必要進行多程序通信。如果沒有本地接口對象,那麼直接new一個

Stub.Proxy(obj)

執行個體傳回。我們來看一下這個

Proxy

類的具體實作

private static class Proxy implements com.cmeiyuan.pluginstudy.IMedia {
    private android.os.IBinder mRemote;

    Proxy(android.os.IBinder remote) {
        mRemote = remote;
    }

    @Override
    public boolean start() throws android.os.RemoteException {
        android.os.Parcel _data = android.os.Parcel.obtain();
        android.os.Parcel _reply = android.os.Parcel.obtain();
        boolean _result;
        try {
            _data.writeInterfaceToken(DESCRIPTOR);
            mRemote.transact(Stub.TRANSACTION_start, _data, _reply, );
            _reply.readException();
            _result = ( != _reply.readInt());
        } finally {
            _reply.recycle();
            _data.recycle();
        }
        return _result;
    }
}
           

這個

Proxy

類是

Stub

類的内部類,其實就是對

obj

的一層封裝(代理),那麼我們通過這個代理類,就很容易和

Server

端進行通信了。具體實作也很簡單,就是調用遠端Binder對象

mRemote

transact()

方法,将資料發送給

Server

端。

Stub

是一個抽象類,繼承于

Binder

類,實作了父類的

onTransact()

方法,

onTransact()

方法的作用是執行Server端的操作,并将操作的結果傳回給Client端。一般情況,我們需要寫一個子類繼承于

Stub

類,然後實作

IMedia

的接口方法,這些方法會被父類

onTransact()

方法調用到。

3.總結一下多程序通信過程:

(1)通過

bindService()

得到遠端

Binder

對象

obj

(2)通過

IMedia.Stub.asInterface(obj)

得到一個遠端代理類

Sub.Proxy

對象

mediaRemoteProxy

(3)調用

mediaRemoteProxy.start()

方法,事實上是調用了其内部遠端Binder對象

mRemote

transact()

方法,将資料發送給

Server

(4)服務端收到

Client

端發送過來的資料時

onTransact()

會被調用,有一個子類繼承于

IMedia.Stub

類,實作了

start()

方法,

onTransact()

被調用時,自然調用到了子類裡的

start()

方法。換言之,

Server

端的操作被

Client

調用執行了。

4.

start()

方法是在哪個線程被執行的
private IMedia.Stub stub = new IMedia.Stub() {
    @Override
    public boolean start() throws RemoteException {
        Log.d("cmy", "media start:" + Thread.currentThread().getId());
        return true;
    }
};
           

通過調試程式發現

start()

方法被執行在名為

binder1

binder2

的線程中,而不是

主線程

。這也解釋了系統的

ActivityManagerService

向應用程序發送消息時,需要使用

H

類把消息轉發到

UI線程

,而且也必須這麼做,因為

UI線程

調用了

Looper.loop()

開啟了循環,線程是被阻塞的。