在 Android 系統(tǒng)中阔拳,Binder 起著非常重要的作用崭孤,它是整個系統(tǒng) IPC 的基石类嗤。網(wǎng)上已經(jīng)有很多文章講述 Binder 的原理糊肠,有的講的比較淺顯,沒有觸及到關(guān)鍵遗锣,有的講的太過于深入底層货裹,難以理解,本文會比較全面精偿,以一個比較輕松的方式弧圆,從面到點,大處著眼笔咽,小處著手的形式去講述 Binder 在 Android 中是如何使用的搔预。理解 Binder 的基本原理,對學習 Android 也有很大的幫助叶组,很多問題也能夠得到解釋拯田,例如 ContentProvider 中的 CRUD 是否是線程安全的?又例如在使用 AIDL 的時候甩十,在 Service 中實現(xiàn)的接口是否是線程安全的船庇?
本文分為以下幾個部分去介紹
- Android 整體架構(gòu)
- Binder IPC 的架構(gòu)
- 手動實現(xiàn) Binder IPC
- 使用 AIDL 實現(xiàn) Binder IPC
如果覺得文章太長吭产,可以先只看「小結(jié)」部分,小結(jié)會把每個部分的重點總結(jié)出來鸭轮,有些部分可以跳過臣淤。
Android 整體架構(gòu)
不識廬山真面目,只緣身在此山中窃爷,所以我們先來大概看下 Android 這座大山的整體輪廓邑蒋。我們先從 Android 的整體架構(gòu)來看看 Binder 是處于什么地位,這張圖引自 Android 項目開源網(wǎng)站:https://source.android.com
從下往上依次為
- 內(nèi)核層:Linux 內(nèi)核和各類硬件設備的驅(qū)動按厘,這里需要注意的是寺董,Binder IPC 驅(qū)動也是在這一層實現(xiàn),比較特殊
- 硬件抽象層:封裝「內(nèi)核層」硬件驅(qū)動刻剥,提供可供「系統(tǒng)服務層」調(diào)用的統(tǒng)一硬件接口
- 系統(tǒng)服務層:提供核心服務遮咖,并且提供可供「應用程序框架層」調(diào)用的接口
- Binder IPC 層:作為「系統(tǒng)服務層」與「應用程序框架層」的 IPC 橋梁,互相傳遞接口調(diào)用的數(shù)據(jù)造虏,實現(xiàn)跨進層的通訊
- 應用程序框架層:這一層可以理解為 Android SDK御吞,提供四大組件,View 繪制體系等平時開發(fā)中用到的基礎(chǔ)部件
在一個大的項目里面漓藕,分層是非常重要的陶珠,處于最底層的接口最具有「通用性」,接口粒度最細享钞,越往上層通用性降低揍诽。理論上來說上面的每一層都可以「開放」給開發(fā)者調(diào)用,例如開發(fā)者可以直接調(diào)用硬件抽象層的接口去操作硬件栗竖,或者直接調(diào)用系統(tǒng)服務層中的接口去直接操作系統(tǒng)服務暑脆,甚至是像 Windows 開發(fā)一樣,開發(fā)者可以在內(nèi)核層寫程序狐肢,運行在內(nèi)核中添吗。不過開放帶來的問題就是開發(fā)者權(quán)利太大,對于系統(tǒng)的穩(wěn)定性是沒有任何好處的份名,一個病毒制作者寫了一個內(nèi)核層的病毒碟联,系統(tǒng)也許永遠也起不來了。所以谷歌的做法是將開發(fā)者的權(quán)利收攏到了「應用程序框架層」僵腺,開發(fā)者只能調(diào)用這一層提供的接口鲤孵。
上面的層次中,內(nèi)核層與硬件抽象層均用 C/C++ 實現(xiàn)辰如,系統(tǒng)服務層是以 Java 實現(xiàn)普监,硬件抽象層編譯為 so 文件,以 JNI 的形式供系統(tǒng)服務層使用。系統(tǒng)服務層中的服務隨系統(tǒng)的啟動而啟動鹰椒,只要不關(guān)機锡移,就會一直運行。這些服務干什么事情呢漆际?其實很簡單淆珊,就是完成一個手機該有的核心功能如短信的收發(fā)管理、電話的接聽奸汇、掛斷以及應用程序的包管理施符、Activity 的管理等等。每一個服務均運行在一個獨立進程中擂找,因為是以 Java 實現(xiàn)戳吝,所以本質(zhì)上來說就是運行在一個獨立進程的 Dalvik 虛擬機中。問題就來了贯涎,開發(fā)者的 APP 運行在一個新的進程空間听哭,如何調(diào)用到系統(tǒng)服務層中的接口呢?答案是 IPC(Inter-Process Communication)塘雳,進程間通訊陆盘,縮寫與 RPC(Remote Procedure Call)是不一樣的,實現(xiàn)原理也是不一樣的败明。每一個系統(tǒng)服務在應用層序框架層都有一個 Manager 與之對應隘马,方便開發(fā)者調(diào)用其相關(guān)的功能,具體關(guān)系大致如下
IPC 的方式有很多種妻顶,例如 socket酸员、共享內(nèi)存、管道讳嘱、消息隊列等等幔嗦,我們就不去深究為何要使用 Binder 而不使用其他方式去做,到目前為止呢燥,這座大山的面目算是有個大概的輪廓了崭添。
小結(jié)
- Android 從下而上分了內(nèi)核層寓娩、硬件抽象層叛氨、系統(tǒng)服務層、Binder IPC 層棘伴、應用程序框架層
- Android 中「應用程序框架層」以 SDK 的形式開放給開發(fā)者使用寞埠,「系統(tǒng)服務層」中的核心服務隨系統(tǒng)啟動而運行,通過應用層序框架層提供的 Manager 實時為應用程序提供服務調(diào)用焊夸。系統(tǒng)服務層中每一個服務運行在自己獨立的進程空間中仁连,應用程序框架層中的 Manager 通過 Binder IPC 的方式調(diào)用系統(tǒng)服務層中的服務。
這一小節(jié)完了,看個美女放松下饭冬,繼續(xù)下一小節(jié)
Binder IPC 的架構(gòu)
下面我們就來看看 Binder IPC 的架構(gòu)是怎樣的
Binder IPC 屬于 C/S 結(jié)構(gòu)使鹅,Client 部分是用戶代碼,用戶代碼最終會調(diào)用 Binder Driver 的 transact 接口昌抠,Binder Driver 會調(diào)用 Server患朱,這里的 Server 與 service 不同,可以理解為 Service 中 onBind 返回的 Binder 對象炊苫,請注意區(qū)分下裁厅。
- Client:用戶需要實現(xiàn)的代碼,如 AIDL 自動生成的接口類
- Binder Driver:在內(nèi)核層實現(xiàn)的 Driver
- Server:這個 Server 就是 Service 中 onBind 返回的 IBinder 對象
需要注意的是侨艾,上面綠色的色塊部分都是屬于用戶需要實現(xiàn)的部分执虹,而藍色部分是系統(tǒng)去實現(xiàn)了。也就是說 Binder Driver 這塊并不需要知道唠梨,Server 中會開啟一個線程池去處理客戶端調(diào)用袋励。為什么要用線程池而不是一個單線程隊列呢?試想一下当叭,如果用單線程隊列插龄,則會有任務積壓,多個客戶端同時調(diào)用一個服務的時候就會有來不及響應的情況發(fā)生科展,這是絕對不允許的均牢。
對于調(diào)用 Binder Driver 中的 transact 接口,客戶端可以手動調(diào)用才睹,也可以通過 AIDL 的方式生成的代理類來調(diào)用徘跪,服務端可以繼承 Binder 對象,也可以繼承 AIDL 生成的接口類的 Stub 對象琅攘。這些細節(jié)下面繼續(xù)接著說垮庐,這里暫時不展開。
切記坞琴,這里 Server 的實現(xiàn)是線程池的方式哨查,而不是單線程隊列的方式,區(qū)別在于剧辐,單線程隊列的話寒亥,Server 的代碼是線程安全的,線程池的話荧关,Server 的代碼則不是線程安全的溉奕,需要開發(fā)者自己做好多線程同步。
小結(jié)
- Binder IPC 屬于 C/S 架構(gòu)忍啤,包括 Client加勤、Driver、Server 三個部分
- Client 可以手動調(diào)用 Driver 的 transact 接口,也可以通過 AIDL 生成的 Proxy 調(diào)用
- Server 中會啟動一個「線程池」來處理 Client 的調(diào)用請求鳄梅,處理完成后將結(jié)果返回給 Driver叠国,Driver 再返回給 Client
這里就回答了開篇提問的兩個問題:Service 中通過 AIDL 提供的接口并不是線程安全的,同理 ContentProvider 底層也是使用 Binder戴尸,同樣不是線程安全的煎饼,至于是否需要做多線程保護,看業(yè)務而定校赤,最好是做好多線程同步吆玖,以防萬一。
手動實現(xiàn) Binder IPC
通過上面的講解马篮,大家應該對整體的流程已經(jīng)有了清楚的認識沾乘,下面我們先來看看如何手動實現(xiàn) Binder IPC,即不使用 AIDL 的方式浑测。對應上面的 Client翅阵、Driver、Server迁央,在 Activity掷匠、Service 中分別是什么呢?
上文說的 Server 其實就是 Service 中 onBind 返回的 IBinder 對象岖圈。
Server
假如我們要做一個上報數(shù)據(jù)的功能讹语,運行在 Service 中,在后臺上報數(shù)據(jù)蜂科,接口定義如下
public interface IReporter {
int report(String values, int type);
}
那如何拿到它的 Server 對象呢顽决?答案是通過 Service 的 onBind 方法返回,實現(xiàn)如下
BindService.java
public class BinderService extends Service {
public static final int REPORT_CODE = 0;
public interface IReporter {
int report(String values, int type);
}
public final class Reporter extends Binder implements IReporter {
@Override
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
return super.onTransact(code, data, reply, flags);
}
@Override
public int report(String values, int type) {
return type;
}
}
private Reporter mReporter;
public BinderService() {
mReporter = new Reporter();
}
@Override
public IBinder onBind(Intent intent) {
return mReporter;
}
}
這里我暫時不寫 onTransact 的實現(xiàn)部分导匣,最主要的是繼承 Binder 對象才菠,這個是 Android SDK 提供的基類,它實現(xiàn)了 IBinder 接口贡定,并且封裝了底層的 Binder Driver赋访,看看它是如何初始化的
Binder.java
public Binder() {
init();
...
}
...
private native final void init();
這里調(diào)用 native 的一個 init 方法,我們就不去深究了缓待,知道它是對底層的 Binder Driver 的封裝即可蚓耽。當客戶端發(fā)起請求的時候,Binder Driver 會調(diào)用它的 execTransact 方法命斧,并在內(nèi)部調(diào)用到 onTransact 方法田晚,用戶端代碼可以重載該方法去實現(xiàn)自己的業(yè)務邏輯代碼。我們的實現(xiàn)方式如下
BindService.java
protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
switch (code) {
case REPORT_CODE:
data.enforceInterface("reporter");
String values = data.readString();
Log.i("IReporter", "data is '" + values + "'");
int type = data.readInt();
int result = report(values, type);
reply.writeInterfaceToken("reporter");
reply.writeInt(result);
return true;
}
return super.onTransact(code, data, reply, flags);
}
這里的主要過程就是:獲取到 data 中傳遞過來的參數(shù) values 和 type国葬,調(diào)用自己實現(xiàn)的 report 函數(shù),將返回值寫到 reply 中。
注意汇四,這里實現(xiàn)的兩個關(guān)鍵點就是
- Reporter 類繼承 Binder 類接奈,重載 onTransact 函數(shù),實現(xiàn)自己的業(yè)務邏輯
- 在 Service 的 onBind 中返回 Reporter 類的實例
這里看不到半點線程池的影子對吧通孽,其實是在 Binder 內(nèi)部的 native 方法中去實現(xiàn)了的序宦,記住你寫的代碼要保持線程安全就對了。
Driver
該部分已經(jīng)被 Binder 類給封裝了背苦,暴露給開發(fā)者的已經(jīng)是很簡單的使用方式了互捌,即繼承 Binder,實現(xiàn) onTransact 即可行剂。
Client
那 Client 是什么呢秕噪?也就是我們想使用 IReport 接口來做數(shù)據(jù)上報的地方,一般都在 Activity 里面厚宰,主要實現(xiàn)如下
MainActivity.java
private IBinder mReporterBind;
private class BindConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mReporterBind = service;
}
@Override
public void onServiceDisconnected(ComponentName name) {
mReporterBind = null;
}
}
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
intent = new Intent(this, BinderService.class);
bindService(intent, new BindConnection(), BIND_AUTO_CREATE);
}
這樣就拿到了后臺 Service 中的 onBind 返回的 IBinder 對象腌巾,也就是上面 Binder IPC 架構(gòu)中的 mRemote 對象。至于 bindService 中做了什么铲觉,有興趣的讀者可以再去研究澈蝙,這里知道它返回的就是 mRemote 對象即可。
MainActivity.java
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken("reporter");
data.writeString("this is a test string.");
data.writeInt(type);
mReporterBind.transact(BinderService.REPORT_CODE, data, reply, 0);
reply.enforceInterface("reporter");
int result = reply.readInt();
data.recycle();
reply.recycle();
通過 Parcel.obtain() 獲取發(fā)送包對象撵幽、應答包對象灯荧,寫入數(shù)據(jù),調(diào)用 IBinder 的 transact 接口盐杂,即 mRemote.transact() 的調(diào)用漏麦。
小結(jié)
- 一切復雜的邏輯均已經(jīng)被封裝在實現(xiàn)了 IBinder 接口的 Binder 類中
- Activity 中通過 bindService 拿到 Binder Driver 中的 mRemote 對象(IBinder 的實例),然后「組包」况褪,然后「調(diào)用 transact 接口」按序發(fā)送數(shù)據(jù)包
- Service 中繼承 Binder 類撕贞,「重載 onTransact 函數(shù)」,實現(xiàn)參數(shù)的「解包」测垛,發(fā)送返回包等捏膨,在 onBind 中返回具體的實現(xiàn)類 如上文中的 Reporter
總的說來就是 Client 組包,調(diào)用 transact 發(fā)送數(shù)據(jù)食侮,Server 接到調(diào)用号涯,解包,返回锯七,下面使用 AIDL 的流程本質(zhì)也是一樣链快。
使用 AIDL 實現(xiàn) Binder IPC
上面的例子我們看到,定義了 IReporter 接口眉尸,但是其實 Client 中并沒有用到域蜗,因為數(shù)據(jù)的組包和解包其實是手動編碼的巨双,并不能直接調(diào)用接口,所以其實定義接口的意義等于 0霉祸,基于此筑累,Android 給了我們更好用的方式那就是 AIDL,定義如下
IReporter.aidl
package com.android.binder;
interface IReporter {
int report(String values, int type);
}
Server
AidlService.java
public class AidlService extends Service {
public static final class Reporter extends IReporter.Stub {
@Override
public int report(String values, int type) throws RemoteException {
return type;
}
}
private Reporter mReporter;
public AidlService() {
mReporter = new Reporter();
}
@Override
public IBinder onBind(Intent intent) {
return mReporter;
}
}
這里與手動實現(xiàn)的方式不同的是丝蹭,一個繼承了 Binder慢宗,一個繼承了 AIDL 自動生成的 Stub 對象,它是什么呢奔穿?我們可以看下它的定義
IReporter.java
public interface IReporter extends android.os.IInterface
{
public static abstract class Stub extends android.os.Binder implements com.android.binder.IReporter {
...
@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_report:
{
data.enforceInterface(DESCRIPTOR);
java.lang.String _arg0;
_arg0 = data.readString();
int _arg1;
_arg1 = data.readInt();
int _result = this.report(_arg0, _arg1);
reply.writeNoException();
reply.writeInt(_result);
return true;
}
}
return super.onTransact(code, data, reply, flags);
}
}
...
}
其實和我們上文的寫法是一樣的镜沽,自動生成的 IReporter 類自動給我們處理了一些參數(shù)的組包和解包而已,在 case 語句中調(diào)用了 this.report 即可調(diào)用到自己的業(yè)務邏輯部分了贱田。
Driver
與上文一致缅茉,還是 Binder 的內(nèi)部封裝
Client
MainActivity.java
private IReporter mReporterAidl;
private class AidlConnection implements ServiceConnection {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mReporterAidl = IReporter.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
mReporterAidl = null;
}
}
...
@Override
protected void onCreate(Bundle savedInstanceState) {
...
Intent intent = new Intent(this, AidlService.class);
bindService(intent, new AidlConnection(), BIND_AUTO_CREATE);
}
這里與手動實現(xiàn)方式也有區(qū)別,即調(diào)用了 Stub 對象的 asInterface湘换,具體做了什么呢宾舅?
public static com.android.binder.IReporter asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.android.binder.IReporter))) {
return ((com.android.binder.IReporter)iin);
}
return new com.android.binder.IReporter.Stub.Proxy(obj);
}
先查找本地接口是否存在,判斷是否是本地調(diào)用彩倚,如果是則直接返回 IReporter 的對象筹我,否則返回 Stub.Proxy 對象,這個 Proxy 對象是做什么的呢帆离?
private static class Proxy implements com.android.binder.IReporter
{
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 int report(java.lang.String values, int type) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
int _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(values);
_data.writeInt(type);
mRemote.transact(Stub.TRANSACTION_report, _data, _reply, 0);
_reply.readException();
_result = _reply.readInt();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
基本上已經(jīng)很明了了蔬蕊,就是一個代理對象,對調(diào)用接口參數(shù)做組包而已哥谷,然后調(diào)用了 mRemote.transact 接口岸夯,和上文手動實現(xiàn)的方式是一致的。
小結(jié)
- AIDL 自動生成了 Stub 類
- 在 Service 端繼承 Stub 類们妥,Stub 類中實現(xiàn)了 onTransact 方法實現(xiàn)了「解包」的功能
- 在 Client 端使用 Stub 類的 Proxy 對象猜扮,該對象實現(xiàn)了「組包」并且調(diào)用 transact 的功能
有了 AIDL 之后,IReporter 接口就變得有意義了监婶,Client 調(diào)用接口旅赢,Server 端實現(xiàn)接口,一切「組包」惑惶、「解包」的邏輯封裝在了 Stub 類中煮盼,一切就是那么完美。