一.引言
最近一段時(shí)間由于工作,接觸到framework部分比較多一點(diǎn),也難免要和Binder打一些交道,也整理了一些相關(guān)知識(shí),但準(zhǔn)備寫這篇文章時(shí),還是有些慌。而且關(guān)于整個(gè)Binder機(jī)制的復(fù)雜程度不是三言兩語(yǔ)能描敘清楚的,也害怕自己的理解有些偏差,誤導(dǎo)一些朋友(ps:反正也沒人看....扎心)所以也參考了很多資料。
本文主要站在Android開發(fā)的角度來大致解析下Binder在java層的一些知識(shí)原理,給大家腦子形成一個(gè)完整的概念,比如AIDL的實(shí)現(xiàn)原理,Binder是怎么通信的等等,文章文字較多,請(qǐng)耐心觀看
二.Binder概述
2.1 Android為什么選擇Binder
Android是基于Linux內(nèi)核的,所以Android要實(shí)現(xiàn)進(jìn)程間的通信,其實(shí)大可使用linux原有的一些手段,比如管道,共享內(nèi)存,socket等方式,但是Android還是采用了Binder作為主要機(jī)制,說明Binder具有無可比擬的優(yōu)勢(shì)。
其實(shí)進(jìn)程通信大概就兩個(gè)方面因素,一者性能方面,傳輸效率問題,傳統(tǒng)的管道隊(duì)列模式采用內(nèi)存緩沖區(qū)的方式,數(shù)據(jù)先從發(fā)送方緩存區(qū)拷貝到內(nèi)核開辟的緩存區(qū)中,然后再?gòu)膬?nèi)核緩存區(qū)拷貝到接收方緩存區(qū),至少有兩次拷貝過程,而socket都知道傳輸效率低,開銷大,用于跨網(wǎng)絡(luò)進(jìn)程交互比較多,共享內(nèi)存雖然無需拷貝。
二者這是安全問題,Android作為一個(gè)開放式,擁有眾多開發(fā)者的的平臺(tái),應(yīng)用程序的來源廣泛,確保終端安全是非常重要的,傳統(tǒng)的IPC通信方式?jīng)]有任何措施,基本依靠上層協(xié)議,其一無法確認(rèn)對(duì)方可靠的身份,Android為每個(gè)安裝好的應(yīng)用程序分配了自己的UID,故進(jìn)程的UID是鑒別進(jìn)程身份的重要標(biāo)志,傳統(tǒng)的IPC要發(fā)送類似的UID也只能放在數(shù)據(jù)包里,但也容易被攔截,惡意進(jìn)攻,socket則需要暴露自己的ip和端口,知道這些惡意程序則可以進(jìn)行任意接入。
綜上所述,Android需要一種高效率,安全性高的進(jìn)程通信方式,也就是Binder,Binder只需要一次拷貝,性能僅次于共享內(nèi)存,而且采用的傳統(tǒng)的C/S結(jié)構(gòu),穩(wěn)定性也是沒得說,發(fā)送添加UID/PID,安全性高。
2.2 Binder實(shí)現(xiàn)機(jī)制
2.2.1進(jìn)程隔離
我們知道進(jìn)程之間是無法直接進(jìn)行交互的,每個(gè)進(jìn)程獨(dú)享自己的數(shù)據(jù),而且操作系統(tǒng)為了保證自身的安全穩(wěn)定性,將系統(tǒng)內(nèi)核空間和用戶空間分離開來,保證用戶程序進(jìn)程崩潰時(shí)不會(huì)影響到整個(gè)系統(tǒng),簡(jiǎn)單的說就是,內(nèi)核空間(Kernel)是系統(tǒng)內(nèi)核運(yùn)行的空間,用戶空間(UserSpace)是用戶程序運(yùn)行的空間。為了保證安全性,它們之間是隔離的,所以用戶空間的進(jìn)程要進(jìn)行交互需要通過內(nèi)核空間來驅(qū)動(dòng)整個(gè)過程。
2.2.2 C/S結(jié)構(gòu)
Binder是基于C/S機(jī)制的,要實(shí)現(xiàn)這樣的機(jī)制,server必須需要有特定的節(jié)點(diǎn)來接受到client的請(qǐng)求,也就是入口地址,像輸入一個(gè)網(wǎng)址,通過DNS解析出對(duì)應(yīng)的ip,然后進(jìn)行訪問,這個(gè)就是server提供出來的節(jié)點(diǎn)地址,而Binder而言的話,與傳統(tǒng)的C/S不太一樣,Binder本身來作為Server中提供的節(jié)點(diǎn),client拿到Binder實(shí)體對(duì)象對(duì)應(yīng)的地址去訪問Server,對(duì)于client而言,怎么拿到這個(gè)地址并建立起整個(gè)通道是整個(gè)交互的關(guān)鍵所在,而且Binder作為一個(gè)Server中的實(shí)體,對(duì)象提供一系列的方法來實(shí)現(xiàn)服務(wù)端和客戶端之間的請(qǐng)求,只要client拿到這個(gè)引用就可以或者一個(gè)有著該方法代理對(duì)象的引用,就可以進(jìn)行通信了。
引用Android Bander設(shè)計(jì)與實(shí)現(xiàn) - 設(shè)計(jì)篇一段話來總結(jié):
面向?qū)ο笏枷氲囊雽⑦M(jìn)程間通信轉(zhuǎn)化為通過對(duì)某個(gè)Binder對(duì)象的引用調(diào)用該對(duì)象的方法,而其獨(dú)特之處在于Binder對(duì)象是一個(gè)可以跨進(jìn)程引用的對(duì)象,它的實(shí)體位于一個(gè)進(jìn)程中,而它的引用卻遍布于系統(tǒng)的各個(gè)進(jìn)程之中。最誘人的是,這個(gè)引用和java里引用一樣既可以是強(qiáng)類型,也可以是弱類型,而且可以從一個(gè)進(jìn)程傳給其它進(jìn)程,讓大家都能訪問同一Server,就象將一個(gè)對(duì)象或引用賦值給另一個(gè)引用一樣。Binder模糊了進(jìn)程邊界,淡化了進(jìn)程間通信過程,整個(gè)系統(tǒng)仿佛運(yùn)行于同一個(gè)面向?qū)ο蟮某绦蛑小P涡紊腂inder對(duì)象以及星羅棋布的引用仿佛粘接各個(gè)應(yīng)用程序的膠水,這也是Binder在英文里的原意。
2.2.3 Binder通信模型
Binder基于C/S的結(jié)構(gòu)下,定義了4個(gè)角色:Server、Client、ServerManager、Binder驅(qū)動(dòng),其中前三者是在用戶空間的,也就是彼此之間無法直接進(jìn)行交互,Binder驅(qū)動(dòng)是屬于內(nèi)核空間的,屬于整個(gè)通信的核心,雖然叫驅(qū)動(dòng),但是實(shí)際上和硬件沒有太大關(guān)系,只是實(shí)現(xiàn)的方式和驅(qū)動(dòng)差不多,驅(qū)動(dòng)負(fù)責(zé)進(jìn)程之間Binder通信的建立,Binder在進(jìn)程之間的傳遞,Binder引用計(jì)數(shù)管理,數(shù)據(jù)包在進(jìn)程之間的傳遞和交互等一系列底層支持。
ServerManager的作用?
我們知道ServerManager也是屬于用戶空間的一個(gè)進(jìn)程,主要作用就是作為Server和client的橋梁,client可以ServerManager拿到Server中Binder實(shí)體的引用,這么說可能有點(diǎn)模糊,舉個(gè)簡(jiǎn)單的例子,我們?cè)L問,www.baidu.com,百度首頁(yè)頁(yè)面就顯示出來了,首先我們知道,這個(gè)頁(yè)面肯定是發(fā)布在百度某個(gè)服務(wù)器上的,DNS通過你這個(gè)地址,解析出對(duì)應(yīng)的ip地址,再去訪問對(duì)應(yīng)的頁(yè)面,然后再把數(shù)據(jù)返回給客戶端,完成交互。這個(gè)和Binder的C/S非常類似,這里的DNS就是對(duì)應(yīng)的ServerManager,首先,Server中的Binder實(shí)體對(duì)象,將自己的引用(也就是ip地址)注冊(cè)到ServerManager,client通過特定的key(也就是百度這個(gè)網(wǎng)址)和這個(gè)引用進(jìn)行綁定,ServerManager內(nèi)部自己維護(hù)一個(gè)類似MAP的表來一一對(duì)應(yīng),通過這個(gè)key就可以向ServerManager拿到Server中Binder的引用,對(duì)應(yīng)到Android開發(fā)中,我們知道很多系統(tǒng)服務(wù)都是通過Binder去和AMS進(jìn)行交互的,比如獲取音量服務(wù):
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
細(xì)心的朋友應(yīng)該發(fā)現(xiàn)ServerManager和Server也是兩個(gè)不同的進(jìn)程呀,Server要向ServerManager去注冊(cè)不是也要涉及到進(jìn)程間的通信嗎,當(dāng)前實(shí)現(xiàn)進(jìn)程間通信又要用到進(jìn)程間的通信,你這不是扯犢子嗎....莫急莫急,Binder的巧妙之處在于,當(dāng)ServerManager作為Serve端的時(shí)候,它提供的Binder比較特殊,它沒有名字也不需要注冊(cè),當(dāng)一個(gè)進(jìn)程使用BINDER_SET_CONTEXT_MGR命令將自己注冊(cè)成SMgr時(shí)Binder驅(qū)動(dòng)會(huì)自動(dòng)為它創(chuàng)建Binder實(shí)體,這個(gè)Binder的引用在所有Client中都固定為0而無須通過其它手段獲得。也就是說,一個(gè)Server若要向ServerManager注冊(cè)自己Binder就必需通過0這個(gè)引用號(hào)和ServerManager的Binder通信,有朋友又要問了,server和client屬于兩個(gè)不同的進(jìn)程,client怎么能拿到server中對(duì)象,不妨先看看下面的交互圖
從上圖很清晰的可以看出來整個(gè)的交互過程,原本從SM中拿到binder的引用,通過Binder驅(qū)動(dòng)層的處理之后,返回給了client一個(gè)代理對(duì)象,實(shí)際上如果client和server處于同一個(gè)進(jìn)程,返回的就是當(dāng)前binder對(duì)象,如果client和server不處于同一個(gè)進(jìn)程,返回給client的就是一個(gè)代理對(duì)象,這一點(diǎn),有興趣可以看下源碼。
2.2.4 Binder角色的定位
Binder本質(zhì)上只是提供了一種通信的方式,和我們具體要實(shí)現(xiàn)的內(nèi)容沒有關(guān)系,為了實(shí)現(xiàn)這個(gè)服務(wù),我們需要定義一些接口,讓client能夠遠(yuǎn)程調(diào)用服務(wù),因?yàn)槭强邕M(jìn)程,這時(shí)候就要設(shè)計(jì)到代理模式,以接口函數(shù)位基準(zhǔn),client和server去實(shí)現(xiàn)接口函數(shù),Server是服務(wù)真正的實(shí)現(xiàn),client作為一個(gè)遠(yuǎn)程的調(diào)用。
- 從Server進(jìn)程來看,Binder是存在的實(shí)體對(duì)象,client通過transact()函數(shù),經(jīng)過Binder驅(qū)動(dòng),最終回調(diào)到Binder實(shí)體的onTransact()函數(shù)中。
- 從 Client進(jìn)程的角度看,Binder 指的是對(duì) Binder 代理對(duì)象,是 Binder 實(shí)體對(duì)象的一個(gè)遠(yuǎn)程代理,通過Binder驅(qū)動(dòng)進(jìn)行交互
3.手寫進(jìn)程通信
上面說了那么多,大家伙也看累了,下面通過代碼的形式讓大家對(duì)Binder加深點(diǎn)理解,日常開發(fā)中,涉及到進(jìn)程間通信的話,我們首先想到的可能就是AIDL,但不知道有沒有和我感覺一樣的朋友。。第一次寫AIDL是蒙蔽的,通過.aidl文件,編譯器自動(dòng)生成代碼,生成一個(gè)java文件,里面又有Stub類,里面還有Proxy類,完全不理解里面的機(jī)制,確實(shí)不便于我們理解學(xué)習(xí),為了加深理解,我們拋棄aidl,手寫一個(gè)通信代碼。
首先我們要定義一個(gè)接口服務(wù),也就是上述的服務(wù)端要具備的能力來提供給客戶端,定義一個(gè)接口繼承IInterface,代表了服務(wù)端的能力
public interface PersonManger extends IInterface {
void addPerson(Person mPerson);
List<Person> getPersonList();
}
接下來我們就要定義一個(gè)Server中的Binder實(shí)體對(duì)象了,首先肯定要繼承Binder,其次需要實(shí)現(xiàn)上面定義好的服務(wù)接口
public abstract class BinderObj extends Binder implements PersonManger {
public static final String DESCRIPTOR = "com.example.taolin.hellobinder";
public static final int TRANSAVTION_getPerson = IBinder.FIRST_CALL_TRANSACTION;
public static final int TRANSAVTION_addPerson = IBinder.FIRST_CALL_TRANSACTION + 1;
public static PersonManger asInterface(IBinder mIBinder){
IInterface iInterface = mIBinder.queryLocalInterface(DESCRIPTOR);
if (null!=iInterface&&iInterface instanceof PersonManger){
return (PersonManger)iInterface;
}
return new Proxy(mIBinder);
}
@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 TRANSAVTION_getPerson:
data.enforceInterface(DESCRIPTOR);
List<Person> result = this.getPersonList();
reply.writeNoException();
reply.writeTypedList(result);
return true;
case TRANSAVTION_addPerson:
data.enforceInterface(DESCRIPTOR);
Person arg0 = null;
if (data.readInt() != 0) {
arg0 = Person.CREATOR.createFromParcel(data);
}
this.addPerson(arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
@Override
public IBinder asBinder() {
return this;
}
}
首先我們看asInterface方法,Binder驅(qū)動(dòng)傳來的IBinder對(duì)象,通過queryLocalInterface方法,查找本地Binder對(duì)象,如果返回的就是PersonManger,說明client和server處于同一個(gè)進(jìn)程,直接返回,如果不是,返回給一個(gè)代理對(duì)象。
當(dāng)然作為代理對(duì)象,也是需要實(shí)現(xiàn)服務(wù)接口
public class Proxy implements PersonManger {
private IBinder mIBinder;
public Proxy(IBinder mIBinder) {
this.mIBinder =mIBinder;
}
@Override
public void addPerson(Person mPerson) {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
try {
data.writeInterfaceToken(DESCRIPTOR);
if (mPerson != null) {
data.writeInt(1);
mPerson.writeToParcel(data, 0);
} else {
data.writeInt(0);
}
mIBinder.transact(BinderObj.TRANSAVTION_addPerson, data, replay, 0);
replay.readException();
} catch (RemoteException e){
e.printStackTrace();
} finally {
replay.recycle();
data.recycle();
}
}
@Override
public List<Person> getPersonList() {
Parcel data = Parcel.obtain();
Parcel replay = Parcel.obtain();
List<Person> result = null;
try {
data.writeInterfaceToken(DESCRIPTOR);
mIBinder.transact(BinderObj.TRANSAVTION_getPerson, data, replay, 0);
replay.readException();
result = replay.createTypedArrayList(Person.CREATOR);
}catch (RemoteException e){
e.printStackTrace();
} finally{
replay.recycle();
data.recycle();
}
return result;
}
@Override
public IBinder asBinder() {
return null;
}
}
這里的代理對(duì)象實(shí)質(zhì)就是client最終拿到的代理服務(wù),通過這個(gè)就可以和Server進(jìn)行通信了,首先通過Parcel將數(shù)據(jù)序列化,然后調(diào)用 remote.transact()將方法code,和data傳輸過去,對(duì)應(yīng)的會(huì)回調(diào)在在Server中的onTransact()中
然后是我們的Server進(jìn)程,onBind方法返回mStub對(duì)象,也就是Server中的Binder實(shí)體對(duì)象
public class ServerSevice extends Service {
private static final String TAG = "ServerSevice";
private List<Person> mPeople = new ArrayList<>();
@Override
public void onCreate() {
mPeople.add(new Person());
super.onCreate();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return mStub;
}
private BinderObj mStub = new BinderObj() {
@Override
public void addPerson(Person mPerson) {
if (mPerson==null){
mPerson = new Person();
Log.e(TAG,"null obj");
}
mPeople.add(mPerson);
Log.e(TAG,mPeople.size()+"");
}
@Override
public List<Person> getPersonList() {
return mPeople;
}
};
}
最終我們?cè)诳蛻舳诉M(jìn)程,bindService傳入一個(gè)ServiceConnection對(duì)象,在與服務(wù)端建立連接時(shí),通過我們定義好的BinderObj的asInterface方法返回一個(gè)代理對(duì)象,再調(diào)用方法進(jìn)行交互
public class MainActivity extends AppCompatActivity {
private boolean isConnect = false;
private static final String TAG = "MainActivity";
private PersonManger personManger;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
start();
findViewById(R.id.textView).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (personManger==null){
Log.e(TAG,"connect error");
return;
}
personManger.addPerson(new Person());
Log.e(TAG,personManger.getPersonList().size()+"");
}
});
}
private void start() {
Intent intent = new Intent(this, ServerSevice.class);
bindService(intent,mServiceConnection,Context.BIND_AUTO_CREATE);
}
private ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
Log.e(TAG,"connect success");
isConnect = true;
personManger = BinderObj.asInterface(service);
List<Person> personList = personManger.getPersonList();
Log.e(TAG,personList.size()+"");
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.e(TAG,"connect failed");
isConnect = false;
}
};
}
這樣的話,一次完成的進(jìn)程間的交互就完成了~是不是感覺沒有想象中那么難,最后建議大家在不借助 AIDL 的情況下手寫實(shí)現(xiàn) Client 和 Server 進(jìn)程的通信,加深對(duì) Binder 通信過程的理解。
本文在寫作過程中參考了蠻多的文章和源碼,感謝大佬們的無私奉獻(xiàn),溜了溜了~
參考文章