簡單說Binder

簡單說Binder

Binder算是Android中比較難懂的一部分內(nèi)容了,但是非常的重要,要想研究Framework層無論如何也繞不開Binder诀蓉。網(wǎng)上也有很多講解Binder的文章浸遗,有的很深入涉及到底層C層面,理解起來難度較大驾胆,要完全理解還需要Linux驅(qū)動(dòng)的知識(shí)涣澡,看了還也是似懂非懂,我認(rèn)為也不需要理解那么深入丧诺。寫這篇博客主要是我從最學(xué)習(xí)理解Binder的過程的角度出發(fā)入桂,也來談?wù)凚inder。
Binder是什么
Binder是android中用于進(jìn)程間通信IPC的重要機(jī)制驳阎,ActivityManagerService抗愁、WinderManagerService等系統(tǒng)服務(wù)的背后都是Binder。
Binder架構(gòu)包括服務(wù)器接口呵晚、Binder驅(qū)動(dòng)蜘腌、客戶端接口三個(gè)模塊。
Binder服務(wù)端:一個(gè)Binder服務(wù)端實(shí)際上就是Binder類的對(duì)象饵隙,該對(duì)象一旦創(chuàng)建撮珠,內(nèi)部則會(huì)啟動(dòng)一個(gè)隱藏線程,會(huì)接收Binder驅(qū)動(dòng)發(fā)送的消息金矛,收到消息后芯急,會(huì)執(zhí)行Binder對(duì)象中的onTransact()函數(shù)倘潜,并按照該函數(shù)的參數(shù)執(zhí)行不同的服務(wù)器端代碼。onTransact函數(shù)的參數(shù)是客戶端調(diào)用transact函數(shù)的輸入志于。
Binder驅(qū)動(dòng):任意一個(gè)服務(wù)端Binder對(duì)象被創(chuàng)建時(shí)涮因,同時(shí)會(huì)在Binder驅(qū)動(dòng)中創(chuàng)建一個(gè)mRemote對(duì)象,該對(duì)象也是一個(gè)Binder類伺绽⊙荩客戶端訪問遠(yuǎn)程服務(wù)端都是通過該mRemote對(duì)象。
客戶端:獲取遠(yuǎn)程服務(wù)在Binder驅(qū)動(dòng)中對(duì)應(yīng)的mRemote引用奈应,然后調(diào)用它的transact方法即可向服務(wù)端發(fā)送消息澜掩。


這幅圖展現(xiàn)了Binder框架的大致構(gòu)成,至于里面有一些內(nèi)容需要看完這篇博客才能看懂杖挣。
需要注意的一個(gè)問題:既然客戶端要通過mRemote引用調(diào)用它的transact方法向服務(wù)端發(fā)送消息肩榕,那么客戶端獲取遠(yuǎn)程服務(wù)在Binder中的mRemote引用?
客戶端獲取遠(yuǎn)程服務(wù)在Binder中的mRemote引用有兩種方式:系統(tǒng)服務(wù)和自定義的服務(wù)端程序不一樣惩妇,對(duì)于系統(tǒng)服務(wù)使用Binder就可以實(shí)現(xiàn)服務(wù)端株汉,而我們自定義的服務(wù)必須借助Service來編寫。
》》系統(tǒng)服務(wù)是在系統(tǒng)啟動(dòng)的時(shí)候在SystemServer進(jìn)程的init2函數(shù)中啟動(dòng)ServerThread線程歌殃,在這個(gè)線程中啟動(dòng)了各種服務(wù)乔妈,并且通過調(diào)用ServerManager.addService(String name, IBinder service)將其加入保存起來。ServerManager就相當(dāng)于DNS服務(wù)器氓皱,在查找某個(gè)服務(wù)時(shí)通過調(diào)用ServerManager.getService(String name)函數(shù)就可以獲得遠(yuǎn)程服務(wù)的Binder路召,至于它的具體細(xì)節(jié)可以查看Android啟動(dòng)相關(guān)的源代碼。
》》自定義的服務(wù)必須通過Service來實(shí)現(xiàn)波材。
通過bind函數(shù)綁定一個(gè)遠(yuǎn)程服務(wù)

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public boolean bindService(Intent service, ServiceConnection conn, int flags)

其中第二個(gè)參數(shù)是一個(gè)回調(diào)接口股淡,onServiceConnected的參數(shù)service即為遠(yuǎn)程服務(wù)在Binder驅(qū)動(dòng)中的binder引用。

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public interface ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service);
public void onServiceDisconnected(ComponentName name);
}

使用Binder進(jìn)行IPC通信

實(shí)現(xiàn)如下效果:Activity‘中有一個(gè)按鈕廷区,點(diǎn)擊該按鈕唯灵,將abc和def三個(gè)字母拼接起來,拼接的函數(shù)在另一個(gè)進(jìn)程中躲因。
客戶端

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public class MainActivity extends Activity {
private boolean isBound;
private Button btn_add;
private IBinder mRemote = null;
private ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mRemote = service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};

@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    bind();  
    btn_add = (Button)findViewById(R.id.btn_add);  
    btn_add.setOnClickListener(new OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            String result = null;  
            try {  
                result = strcat("abc", "def");  
            } catch (RemoteException e) {  
                Toast.makeText(MainActivity.this, "error", 0).show();  
                e.printStackTrace();  
            }  
            Toast.makeText(MainActivity.this, result, 0).show();  
        }  
    });  
}  
  
private void bind() {  
    Intent intent = new Intent(MainActivity.this, ComputeService.class);    
    isBound = bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);  
}  
  
private void unbind() {  
    if (isBound) {  
        MainActivity.this.unbindService(serviceConn);  
        isBound = false;  
    }  
}  
  
private String strcat(String x, String y) throws RemoteException {  
    android.os.Parcel _data = android.os.Parcel.obtain();  
    android.os.Parcel _reply = android.os.Parcel.obtain();  
    String _result;  
    try {  
        _data.writeString(x);  
        _data.writeString(y);  
        mRemote.transact(1, _data, _reply, 0);  
        _result = _reply.readString();  
    } finally {  
        _reply.recycle();  
        _data.recycle();  
    }  
    return _result;  
}  

@Override  
protected void onDestroy() {  
    unbind();  
    super.onDestroy();  
}  

}

遠(yuǎn)程服務(wù)端

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public class ComputeService extends Service {

private IBinder binder = new Binder(){  
    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {  
        if (code == 1) {  
            String _arg0;  
            _arg0 = data.readString();  
            String _arg1;  
            _arg1 = data.readString();  
            String _result = this.strcat(_arg0, _arg1);  
            reply.writeString(_result);  
            return true;  
        }  
        return super.onTransact(code, data, reply, flags);  
    };  
      
    public String strcat(String x, String y){  
        return x + y;  
    }  
};  
  
@Override  
public IBinder onBind(Intent arg0) {  
    return binder;  
}  

}

將該service配置在一個(gè)新的進(jìn)程中
[html] view plaincopy

派生到我的代碼片
派生到我的代碼片

<service android:name = "org.qhyuan.binder.ComputeService"
android:process=":remote"/>

點(diǎn)擊按鈕如圖所示彈出提示



接下來分析上面的代碼:
客戶端通過bindService啟動(dòng)遠(yuǎn)程服務(wù)早敬。最終會(huì)由系統(tǒng)回調(diào)傳入的ServiceConnection接口忌傻,因此可以在onServiceConnected函數(shù)中獲得該遠(yuǎn)程服務(wù)所對(duì)應(yīng)的Binder驅(qū)動(dòng)中的引用大脉,接下來想要和遠(yuǎn)程服務(wù)端通信只需調(diào)用該mRemote的transact方法即可。

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException;

code標(biāo)識(shí)要執(zhí)行的動(dòng)作水孩,其實(shí)就是指調(diào)用服務(wù)端的哪個(gè)函數(shù)镰矿。
data是對(duì)輸入?yún)?shù)的打包
reply是對(duì)返回值的打包
writeString<--->readString 客戶端打包一個(gè)Parcel對(duì)象,在服務(wù)端讀取該P(yáng)arcel對(duì)象中打包的數(shù)據(jù)俘种,客戶端的寫入和服務(wù)端的讀取時(shí)對(duì)應(yīng)的秤标。
這里存在的問題:要統(tǒng)一客戶端寫入和服務(wù)端讀取的順序绝淡,當(dāng)然對(duì)于一個(gè)程序員來說,保證這一點(diǎn)是很簡單的苍姜。
接下來調(diào)用mRemote的transact方法會(huì)陷入內(nèi)核態(tài)牢酵,也就是說剩下的都是由系統(tǒng)完成的,binder驅(qū)動(dòng)會(huì)掛起當(dāng)前線程衙猪,將參數(shù)包裹發(fā)給服務(wù)端程序馍乙,在服務(wù)端的onTransact(code, data, reply, flags)函數(shù)里面讀取出包裝的數(shù)據(jù)進(jìn)行處理(數(shù)據(jù)處理的過程也就是根據(jù)code執(zhí)行指定的服務(wù)函數(shù)),然后把執(zhí)行的結(jié)果放入客戶端提供的reply包裹中垫释,然后服務(wù)端向Binder驅(qū)動(dòng)發(fā)送一個(gè)notify消息丝格,喚醒客戶端線程,繼續(xù)執(zhí)行使得客戶端線程從Binder驅(qū)動(dòng)返回到客戶端代碼區(qū)棵譬,再次回到用戶態(tài)显蝌。
使用AIDL
我們也看到了上面使用Binder進(jìn)行IPC通信的時(shí)候代碼比較繁瑣,尤其是客戶端給服務(wù)端發(fā)送消息的打包過程中要保證順序的一致性订咸。當(dāng)然android也給我們提供了一個(gè)比較好的方式曼尊,那就是使用android提供的aidl工具。
AIDL(Android Interface Definition Language)脏嚷,編譯器通過*.aidl文件的描述信息生成符合通信協(xié)議的Java代碼涩禀,我們不需要自己寫這些繁雜的代碼,使用非常方便然眼。只需要建立一個(gè)xxx.aidl文件艾船,這時(shí)在gen目錄下就會(huì)生成對(duì)應(yīng)的java文件

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

package org.qhyuan.aidl;
interface ICompute {
String strcat (String x,String y);
}

這樣使用aidl來實(shí)現(xiàn)上面的功能就可以很簡單了。于是客戶端代碼

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public class MainActivity extends Activity {
private ICompute compute = null;
private boolean isBound;
private Button btn_add;
private ServiceConnection serviceConn = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
compute = ICompute.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};

@Override  
protected void onCreate(Bundle savedInstanceState) {  
    super.onCreate(savedInstanceState);  
    setContentView(R.layout.activity_main);  
    bind();  
    btn_add = (Button)findViewById(R.id.btn_add);  
    btn_add.setOnClickListener(new OnClickListener() {  
        @Override  
        public void onClick(View v) {  
            String result = null;  
            try {  
                result = compute.strcat("abc", "def");  
            } catch (RemoteException e) {  
                Toast.makeText(MainActivity.this, "error", 0).show();  
                e.printStackTrace();  
            }  
            Toast.makeText(MainActivity.this, result, 0).show();  
        }  
    });  
}  
  
private void bind() {  
    Intent intent = new Intent(MainActivity.this, ComputeService.class);    
    isBound = bindService(intent, serviceConn, Context.BIND_AUTO_CREATE);  
}  
  
private void unbind() {  
    if (isBound) {  
        MainActivity.this.unbindService(serviceConn);  
        isBound = false;  
    }  
}  

@Override  
protected void onDestroy() {  
    unbind();  
    super.onDestroy();  
}  

}

服務(wù)端代碼

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public class ComputeService extends Service {

private IBinder binder = new ICompute.Stub() {  
    @Override  
    public String strcat(String x, String y) throws RemoteException {  
        return x+y;  
    }  
};  
  
@Override  
public IBinder onBind(Intent arg0) {  
    return binder;  
}  

}

其他的都不需要改變高每,是不是簡單了很多屿岂?封裝了底層的細(xì)節(jié),使得程序?qū)懫饋砗軆?yōu)美鲸匿。之前手動(dòng)寫的transact函數(shù)和重寫的onTransact函數(shù)也不見蹤影了爷怀。
接下來分析上面的代碼,看看aidl文件到底做了什么带欢,能使得我們編寫程序簡化很多运授。

[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

/*

  • This file is auto-generated. DO NOT MODIFY.
  • Original file: E:\EclipseProject\Binder\src\org\qhyuan\aidl\ICompute.aidl
    */
    package org.qhyuan.aidl;

public interface ICompute extends android.os.IInterface {
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements
org.qhyuan.aidl.ICompute {
private static final java.lang.String DESCRIPTOR = "org.qhyuan.aidl.ICompute";

    /** Construct the stub at attach it to the interface. */  
    public Stub() {  
        this.attachInterface(this, DESCRIPTOR);  
    }  

    /** 
     * Cast an IBinder object into an org.qhyuan.aidl.ICompute interface, 
     * generating a proxy if needed. 
     */  
    public static org.qhyuan.aidl.ICompute asInterface(  
            android.os.IBinder obj) {  
        if ((obj == null)) {  
            return null;  
        }  
        android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);  
        if (((iin != null) && (iin instanceof org.qhyuan.aidl.ICompute))) {  
            return ((org.qhyuan.aidl.ICompute) iin);  
        }  
        return new org.qhyuan.aidl.ICompute.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_strcat: {  
            data.enforceInterface(DESCRIPTOR);  
            java.lang.String _arg0;  
            _arg0 = data.readString();  
            java.lang.String _arg1;  
            _arg1 = data.readString();  
            java.lang.String _result = this.strcat(_arg0, _arg1);  
            reply.writeNoException();  
            reply.writeString(_result);  
            return true;  
        }  
        }  
        return super.onTransact(code, data, reply, flags);  
    }  

    private static class Proxy implements org.qhyuan.aidl.ICompute {  
        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 strcat(java.lang.String x,  
                java.lang.String y) 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);  
                _data.writeString(x);  
                _data.writeString(y);  
                mRemote.transact(Stub.TRANSACTION_strcat, _data, _reply, 0);  
                _reply.readException();  
                _result = _reply.readString();  
            } finally {  
                _reply.recycle();  
                _data.recycle();  
            }  
            return _result;  
        }  
    }  

    static final int TRANSACTION_strcat = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);  
}  

public java.lang.String strcat(java.lang.String x, java.lang.String y)  
        throws android.os.RemoteException;  

}

為了看起來方便,這里存在幾個(gè)類及每個(gè)類中的屬性和方法大致簡化如下乔煞。
[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

interface ICompute extends IInterface
{
strcat();

static abstract class Stub extends Binder implements ICompute {  
    static final int TRANSACTION_strcat;  
    static final String DESCRIPTOR;  
    static asInterface();  
    asBinder();  
    onTransact();  

    static class Proxy implements ICompute {  
        IBinder binder;  
        asBinder();  
        getInterfaceDescriptor();  
        strcat();  
    }  
}  

}

首先要明白AIDL的主要設(shè)計(jì)思想吁朦。產(chǎn)生的這個(gè)類的思想是抽象出了一個(gè)接口,接口里面包含我們想實(shí)現(xiàn)的strcat方法渡贾,于是服務(wù)端的Binder來實(shí)現(xiàn)這個(gè)接口逗宜,其實(shí)就是這里的Stub類。然后客戶端在獲得了服務(wù)端在Binder驅(qū)動(dòng)中的Binder引用mRemote后,通過該引用給遠(yuǎn)程服務(wù)端發(fā)送消息纺讲,這是包含在Proxy類的strcat函數(shù)中擂仍。由于有aidl工具生成的代碼所以包裹中的打包數(shù)據(jù)的順序都是一致的。
ICompute類中的strcat函數(shù)不需要實(shí)現(xiàn)熬甚,僅僅是提供接口而已逢渔,具體的在Stub的子類類和Proxy類中實(shí)現(xiàn)。
在Stub子類中具體實(shí)現(xiàn)乡括,一般是在服務(wù)端程序中的复局。
在Proxy類中的strcat函數(shù)包括對(duì)參數(shù)的打包和通過Binder驅(qū)動(dòng)中的mRemote調(diào)用transact函數(shù)向服務(wù)端發(fā)送包裹,再從reply包裹中讀取出返回值返回粟判。
》》TRANSACTION_strcat:是對(duì)函數(shù)的編號(hào)亿昏,由于這里只有一個(gè)函數(shù)stract,所以只有這一個(gè)整型值档礁。
》》DESCRIPTOR:每個(gè)Stub類有一個(gè)描述符角钩,與它實(shí)現(xiàn)的接口有關(guān)。
》》onTransact:收到Binder驅(qū)動(dòng)發(fā)來的包裹呻澜,進(jìn)行解包递礼,這里面調(diào)用了this.strcat(_arg0, _arg1);是非常重要的,實(shí)際上是在服務(wù)器端實(shí)現(xiàn)的類的strcat函數(shù)羹幸。
》》asInterface:是最重要的一個(gè)函數(shù)脊髓,這是一個(gè)靜態(tài)方法,是用在客戶端將一個(gè)IBinder對(duì)象轉(zhuǎn)化為它實(shí)現(xiàn)的接口栅受。
如果能根據(jù)DESCRIPTION通過queryLocalInterface查找到的值将硝,就直接返回該值,(如果不是因?yàn)槭莝tatic的屏镊,是不是返回this就可以了依疼?)這對(duì)應(yīng)的情況是服務(wù)端自己調(diào)用該Binder中的服務(wù)程序。返回的就是服務(wù)端的Binder而芥,接下來的調(diào)用就是直接用服務(wù)端的Binder調(diào)用服務(wù)端的程序律罢,不存在IPC。否則就將該IBinder包裝成一個(gè)新的類Proxy類棍丐,接下來調(diào)用Proxy的stract方法實(shí)質(zhì)上是用的Binder驅(qū)動(dòng)中的遠(yuǎn)程Binder的引用mRemote來調(diào)用的误辑,是IPC。這里歌逢,Proxy顧名思義是代理的意思巾钉,本地調(diào)用就直接返回ICompute接口實(shí)際上是當(dāng)前服務(wù)器端的Binder,否則就返回一個(gè)代理類趋翻,該代理類實(shí)現(xiàn)了ICompute睛琳,里面封裝的是Binder驅(qū)動(dòng)中的mRemote引用盒蟆,這樣保證接下來的操作是一致的踏烙。
一句話就是說asInterface函數(shù)的存在將本地調(diào)用和進(jìn)程間調(diào)用綜合在一起了师骗。看到這里有沒有覺得三個(gè)類組織的非常巧妙代碼很優(yōu)美呢讨惩。
另辟癌,上面三個(gè)類如果寫成三個(gè)類而不是寫成嵌套類的形式會(huì)好理解很多。并且和最開始手工寫的Binder本質(zhì)上是一致的荐捻。
代碼中出現(xiàn)的如下幾個(gè)打包的數(shù)據(jù)可以認(rèn)為是增加安全性和處理一些其他的問題黍少,沒有也是可以可以的。writeInterfaceToken<--->enforceInterface 客戶端封裝一個(gè)String標(biāo)識(shí)处面,在服務(wù)端收到后對(duì)比確保該Binder驅(qū)動(dòng)確實(shí)是想訪問我厂置。類似的還有writeException<--->readException。
再此基礎(chǔ)上去看系統(tǒng)中一些XXXManager代碼魂角,就會(huì)容易很多昵济,里面使用Binder的部分都類似于AIDL產(chǎn)生的那些代碼,本質(zhì)上就是上面講的Binder進(jìn)行IPC通信野揪,下面舉例子說明源代碼中使用Binder的地方访忿。
系統(tǒng)服務(wù)中的Binder分析
下面以ServiceManager和ActivityManagerService為例來分析。ServiceManager:
前面已經(jīng)提到過ServiceManager可以認(rèn)為是DNS斯稳,用來查找系統(tǒng)服務(wù)海铆。保存了已經(jīng)開啟的系統(tǒng)服務(wù)。他有兩個(gè)主要的方法
[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public static IBinder getService(String name)
public static void addService(String name, IBinder service)

實(shí)際上ServerManager既是系統(tǒng)服務(wù)的管理者挣惰,同時(shí)也是一個(gè)系統(tǒng)服務(wù)卧斟。因此它肯定是基于Binder實(shí)現(xiàn)的。
接下來的分析中憎茂,時(shí)刻記得使用aidl工具生成那三個(gè)類:IXXX唆涝、IXXX.Stub和IXXX.Stub.Proxy,并做好對(duì)應(yīng)唇辨。這樣看ServiceManager的相關(guān)的代碼就容易多了廊酣。1.與IXXX相對(duì)應(yīng)的類就是IServiceManager類,封裝了遠(yuǎn)程調(diào)用的幾個(gè)主要函數(shù)赏枚。
[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public interface IServiceManager extends IInterface
{
public IBinder getService(String name) throws RemoteException;
public IBinder checkService(String name) throws RemoteException;
public void addService(String name, IBinder service, boolean allowIsolated)
throws RemoteException;
public String[] listServices() throws RemoteException;
public void setPermissionController(IPermissionController controller)
throws RemoteException;
static final String descriptor = "android.os.IServiceManager";
int GET_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION;
int CHECK_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+1;
int ADD_SERVICE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+2;
int LIST_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+3;
int CHECK_SERVICES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+4;
int SET_PERMISSION_CONTROLLER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+5;
}

2.與IXXX.Stub對(duì)應(yīng)的類就是ServiceManagerNative亡驰。[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public abstract class ServiceManagerNative extends Binder implements IServiceManager{
static public IServiceManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IServiceManager in = (IServiceManager)obj.queryLocalInterface(descriptor);
if (in != null) {
return in;
}
return new ServiceManagerProxy(obj);
}

public ServiceManagerNative() {  
    attachInterface(this, descriptor);  
}  
  
public boolean onTransact(int code, Parcel data, Parcel reply, int flags){  
    try {  
        switch (code) {  
        case IServiceManager.GET_SERVICE_TRANSACTION: {  
            data.enforceInterface(IServiceManager.descriptor);  
            String name = data.readString();  
            IBinder service = getService(name);  
            reply.writeStrongBinder(service);  
            return true;  
        }  
        case IServiceManager.ADD_SERVICE_TRANSACTION: {  
            data.enforceInterface(IServiceManager.descriptor);  
            String name = data.readString();  
            IBinder service = data.readStrongBinder();  
            boolean allowIsolated = data.readInt() != 0;  
            addService(name, service, allowIsolated);  
            return true;  
        }  
        // ...  
    } catch (RemoteException e) {  
    }  
      
    return false;  
}  
public IBinder asBinder() {  
    return this;  
}  

}

3.與IXXX.Stub.Proxy對(duì)應(yīng)的類ServiceManagerProxy[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

class ServiceManagerProxy implements IServiceManager {
public ServiceManagerProxy(IBinder remote) {
mRemote = remote;
}

public IBinder asBinder() {  
    return mRemote;  
}  
  
public IBinder getService(String name) throws RemoteException {  
    Parcel data = Parcel.obtain();  
    Parcel reply = Parcel.obtain();  
    data.writeInterfaceToken(IServiceManager.descriptor);  
    data.writeString(name);  
    mRemote.transact(GET_SERVICE_TRANSACTION, data, reply, 0);  
    IBinder binder = reply.readStrongBinder();  
    reply.recycle();  
    data.recycle();  
    return binder;  
}  
public void addService(String name, IBinder service, boolean allowIsolated) throws RemoteException {  
    Parcel data = Parcel.obtain();  
    Parcel reply = Parcel.obtain();  
    data.writeInterfaceToken(IServiceManager.descriptor);  
    data.writeString(name);  
    data.writeStrongBinder(service);  
    data.writeInt(allowIsolated ? 1 : 0);  
    mRemote.transact(ADD_SERVICE_TRANSACTION, data, reply, 0);  
    reply.recycle();  
    data.recycle();  
}  
// ....  
private IBinder mRemote;  

}

觀察上面的代碼,實(shí)際上和使用adil生成的代碼沒什么兩樣饿幅。僅僅是類命名不一樣凡辱,將三個(gè)類分開寫了而已。
不用看源代碼也知道接下來該怎么做了吧栗恩?透乾!當(dāng)然就是在服務(wù)端繼承ServiceManagerNative類實(shí)現(xiàn)里面的相關(guān)方法就能實(shí)現(xiàn)服務(wù)端,然后在客戶端將遠(yuǎn)程服務(wù)端所對(duì)應(yīng)的的Binder封裝成IServiceManager iSm = ServiceManagerNative.asInterface(binder)即可,正常情況下確實(shí)是這樣的乳乌。實(shí)際上捧韵,在源碼中找不到繼承自ServiceManagerNative類的遠(yuǎn)程服務(wù)端類,比如說ServiceManagerService汉操,根本就找不到這樣一個(gè)類再来。原因是SMS在native層被實(shí)現(xiàn)成一個(gè)獨(dú)立的進(jìn)程,是在啟動(dòng)后解析init.rc腳本啟動(dòng)服務(wù)的磷瘤。native層的代碼沒必要去研究芒篷,那么這個(gè)遠(yuǎn)程的Binder怎么獲得呢?系統(tǒng)提供的函數(shù)BinderInternal.getContextObject()來獲得對(duì)應(yīng)的Binder引用采缚。還是ServiceManager比較特殊嘛针炉,畢竟對(duì)于“DNS”來說不得一開機(jī)就啟動(dòng),還與其他“主機(jī)”有點(diǎn)差別扳抽,但是其他的系統(tǒng)服務(wù)就和上面我們想象的那樣是一樣的了篡帕。
這里要說明一點(diǎn),雖然SMS服務(wù)時(shí)在native層摔蓝,獲取遠(yuǎn)程服務(wù)卻并不一定非要在native層實(shí)現(xiàn)赂苗,使用Binder構(gòu)架與是用什么語言沒必然關(guān)系。
當(dāng)然了贮尉,這里的ServiceManagerNative確實(shí)沒什么用拌滋,如果要說有用,就是他的靜態(tài)方法asInterface吧猜谚。但不知道為什么android源碼中還有這個(gè)類的存在败砂,至少這樣讓我們隱約感覺Binder通信的框架就是這樣的,提高了一致性魏铅。
接下來我們看ServiceManager類
[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public final class ServiceManager {
private static final String TAG = "ServiceManager";
private static IServiceManager sServiceManager;
// 本地有緩存
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
private static IServiceManager getIServiceManager() {
if (sServiceManager != null) {
return sServiceManager;
}
// Find the service manager
sServiceManager = ServiceManagerNative.asInterface(BinderInternal.getContextObject());
return sServiceManager;
}
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
if (service != null) {
return service;
} else {
// 其實(shí)是IPC調(diào)用昌犹,具體會(huì)調(diào)用
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
public static void addService(String name, IBinder service) {
try {
getIServiceManager().addService(name, service, false);
} catch (RemoteException e) {
Log.e(TAG, "error in addService", e);
}
}
// ...
}

ServiceManager類其實(shí)就是對(duì)遠(yuǎn)程的SMS服務(wù)的Binder的封裝。使用ServiceManagerNative.asInterface(BinderInternal.getContextObject());將其轉(zhuǎn)化為sServiceManager接口览芳,接下來使用該對(duì)象即可完成IPC調(diào)用斜姥。
舉個(gè)例子比如調(diào)用ServiceManager的getService方法,實(shí)際上會(huì)ServiceManagerProxy走到ServiceManagerProxy類的getService方法里面去沧竟,然后就是向服務(wù)端發(fā)消息铸敏。當(dāng)然這些細(xì)節(jié)就不用考慮了,因?yàn)閍ndroid的Binder機(jī)制封裝的就是這么完美悟泵,你不用關(guān)注底層細(xì)節(jié)杈笔。
ActivityManager:
再看看ActivityManager中的Binder。
IActivityManager對(duì)應(yīng)IXXX接口
ActivityManagerNative對(duì)應(yīng)IXXX.Stub類糕非,繼承自Binder類蒙具。
ActivityManagerProxy對(duì)應(yīng)IXXX.Stub.Proxy類球榆。
那么AMS的服務(wù)端是那個(gè)類呢?沒錯(cuò)禁筏,就是ActivityManagerService類持钉,這個(gè)類繼承自ActivityManagerNative,實(shí)現(xiàn)了IActivityManager接口中的方法用來進(jìn)行IPC融师。
那么只要在客戶端得到了這個(gè)遠(yuǎn)程服務(wù)端的Binder引用就可以進(jìn)行IPC通信了右钾,事實(shí)確實(shí)是這樣的蚁吝。舉個(gè)栗子旱爆,在ActivityThread的attach方法里有下面兩行代碼
[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

IActivityManager mgr = ActivityManagerNative.getDefault();
mgr.attachApplication(mAppThread);

而getDefault方法代碼如下
[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

static public IActivityManager getDefault() {
return gDefault.get();
}
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
// 這里可以看到通過調(diào)用getService方法得到Binder
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};

顯然,ActivityManagerNative.getDefault()就可以得到遠(yuǎn)程的activity的服務(wù)AMS對(duì)應(yīng)的Binder引用窘茁,并且調(diào)用asInterface將其轉(zhuǎn)化為IActivityManager怀伦,接下來調(diào)用接口中的函數(shù)即可和遠(yuǎn)端的AMS通信。一般來說山林,對(duì)于其他的Service一般就是通過ServiceManager來獲取對(duì)應(yīng)的Binder房待。
當(dāng)然,如果還想弄的更清楚點(diǎn)還需要知道這個(gè)系統(tǒng)服務(wù)是在哪里啟動(dòng)和將Binder添加到SystemServer中的驼抹。
在SystemServerr進(jìn)程的init2函數(shù)中啟動(dòng)ServerThread線程桑孩,這個(gè)線程中啟動(dòng)了很多系統(tǒng)服務(wù),而每個(gè)系統(tǒng)服務(wù)都是一個(gè)線程框冀。ServerThread的run方法大概有1000行流椒,里面啟動(dòng)了系統(tǒng)服務(wù),不同的服務(wù)有不同的啟動(dòng)方法明也。
比如這里的AMS是通過調(diào)用context = ActivityManagerService.main(factoryTest)實(shí)現(xiàn)的宣虾。main函數(shù)里面啟動(dòng)了AThread線程。
接下來又調(diào)用了ActivityManagerService.setSystemProcess();
[java] view plaincopy

派生到我的代碼片
派生到我的代碼片

public static void setSystemProcess() {
ActivityManagerService m = mSelf;
ServiceManager.addService("activity", m, true);
ServiceManager.addService("meminfo", new MemBinder(m));
ServiceManager.addService("gfxinfo", new GraphicsBinder(m));
ServiceManager.addService("dbinfo", new DbBinder(m));
// ....
}

這里的m是在AThread線程中new出來的ActivityManagerService實(shí)例温数。至此绣硝,就完成了服務(wù)的啟動(dòng)和向ServiceManager中的添加。當(dāng)然里面有很多細(xì)節(jié)撑刺,這里主要是跟蹤Binder通信的過程鹉胖。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市够傍,隨后出現(xiàn)的幾起案子甫菠,更是在濱河造成了極大的恐慌,老刑警劉巖王带,帶你破解...
    沈念sama閱讀 216,372評(píng)論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淑蔚,死亡現(xiàn)場離奇詭異,居然都是意外死亡愕撰,警方通過查閱死者的電腦和手機(jī)刹衫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門醋寝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人带迟,你說我怎么就攤上這事音羞。” “怎么了仓犬?”我有些...
    開封第一講書人閱讀 162,415評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵嗅绰,是天一觀的道長。 經(jīng)常有香客問我搀继,道長窘面,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,157評(píng)論 1 292
  • 正文 為了忘掉前任叽躯,我火速辦了婚禮财边,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘点骑。我一直安慰自己酣难,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評(píng)論 6 388
  • 文/花漫 我一把揭開白布黑滴。 她就那樣靜靜地躺著憨募,像睡著了一般。 火紅的嫁衣襯著肌膚如雪袁辈。 梳的紋絲不亂的頭發(fā)上菜谣,一...
    開封第一講書人閱讀 51,125評(píng)論 1 297
  • 那天,我揣著相機(jī)與錄音吵瞻,去河邊找鬼葛菇。 笑死,一個(gè)胖子當(dāng)著我的面吹牛橡羞,可吹牛的內(nèi)容都是我干的眯停。 我是一名探鬼主播,決...
    沈念sama閱讀 40,028評(píng)論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼卿泽,長吁一口氣:“原來是場噩夢啊……” “哼莺债!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起签夭,我...
    開封第一講書人閱讀 38,887評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤齐邦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后第租,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體措拇,經(jīng)...
    沈念sama閱讀 45,310評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評(píng)論 2 332
  • 正文 我和宋清朗相戀三年慎宾,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了丐吓。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片浅悉。...
    茶點(diǎn)故事閱讀 39,690評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖券犁,靈堂內(nèi)的尸體忽然破棺而出术健,到底是詐尸還是另有隱情,我是刑警寧澤粘衬,帶...
    沈念sama閱讀 35,411評(píng)論 5 343
  • 正文 年R本政府宣布荞估,位于F島的核電站,受9級(jí)特大地震影響稚新,放射性物質(zhì)發(fā)生泄漏勘伺。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評(píng)論 3 325
  • 文/蒙蒙 一枷莉、第九天 我趴在偏房一處隱蔽的房頂上張望娇昙。 院中可真熱鬧尺迂,春花似錦笤妙、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至膳音,卻和暖如春召衔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背作谭。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評(píng)論 1 268
  • 我被黑心中介騙來泰國打工礁鲁, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壁拉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評(píng)論 2 368
  • 正文 我出身青樓醇蝴,卻偏偏與公主長得像,于是被迫代替她去往敵國和親想罕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子悠栓,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容