最近一段時間因爲工做,接觸到framework部分比較多一點,也不免要和Binder打一些交道,也整理了一些相關知識,但準備寫這篇文章時,仍是有些慌。並且關於整個Binder機制的複雜程度不是三言兩語能描敘清楚的,也懼怕本身的理解有些誤差,誤導一些朋友(ps:反正也沒人看....扎心)因此也參考了不少資料。html
本文主要站在Android開發的角度來大體解析下Binder在java層的一些知識原理,給你們腦子造成一個完整的概念,好比AIDL的實現原理,Binder是怎麼通訊的等等,文章文字較多,請耐心觀看java
熟悉的朋友能夠看看下篇,將介紹Activity的啓動流程以及Android中的Hook技術:linux
Android是基於Linux內核的,因此Android要實現進程間的通訊,其實大可以使用linux原有的一些手段,好比管道,共享內存,socket等方式,可是Android仍是採用了Binder做爲主要機制,說明Binder具備無可比擬的優點。安全
其實進程通訊大概就兩個方面因素,一者性能方面,傳輸效率問題,傳統的管道隊列模式採用內存緩衝區的方式,數據先從發送方緩存區拷貝到內核開闢的緩存區中,而後再從內核緩存區拷貝到接收方緩存區,至少有兩次拷貝過程,而socket都知道傳輸效率低,開銷大,用於跨網絡進程交互比較多,共享內存雖然無需拷貝。bash
兩者這是安全問題,Android做爲一個開放式,擁有衆多開發者的的平臺,應用程序的來源普遍,確保終端安全是很是重要的,傳統的IPC通訊方式沒有任何措施,基本依靠上層協議,其一沒法確認對方可靠的身份,Android爲每一個安裝好的應用程序分配了本身的UID,故進程的UID是鑑別進程身份的重要標誌,傳統的IPC要發送相似的UID也只能放在數據包裏,但也容易被攔截,惡意進攻,socket則須要暴露本身的ip和端口,知道這些惡意程序則能夠進行任意接入。服務器
綜上所述,Android須要一種高效率,安全性高的進程通訊方式,也就是Binder,Binder只須要一次拷貝,性能僅次於共享內存,並且採用的傳統的C/S結構,穩定性也是沒得說,發送添加UID/PID,安全性高。網絡
咱們知道進程之間是沒法直接進行交互的,每一個進程獨享本身的數據,並且操做系統爲了保證自身的安全穩定性,將系統內核空間和用戶空間分離開來,保證用戶程序進程崩潰時不會影響到整個系統,簡單的說就是,內核空間(Kernel)是系統內核運行的空間,用戶空間(UserSpace)是用戶程序運行的空間。爲了保證安全性,它們之間是隔離的,因此用戶空間的進程要進行交互須要經過內核空間來驅動整個過程。socket
Binder是基於C/S機制的,要實現這樣的機制,server必須須要有特定的節點來接受到client的請求,也就是入口地址,像輸入一個網址,經過DNS解析出對應的ip,而後進行訪問,這個就是server提供出來的節點地址,而Binder而言的話,與傳統的C/S不太同樣,Binder自己來做爲Server中提供的節點,client拿到Binder實體對象對應的地址去訪問Server,對於client而言,怎麼拿到這個地址並創建起整個通道是整個交互的關鍵所在,並且Binder做爲一個Server中的實體,對象提供一系列的方法來實現服務端和客戶端之間的請求,只要client拿到這個引用就能夠或者一個有着該方法代理對象的引用,就能夠進行通訊了。ide
引用Android Bander設計與實現 - 設計篇一段話來總結:
面向對象思想的引入將進程間通訊轉化爲經過對某個Binder對象的引用調用該對象的方法,而其獨特之處在於Binder對象是一個能夠跨進程引用的對象,它的實體位於一個進程中,而它的引用卻遍及於系統的各個進程之中。最誘人的是,這個引用和java裏引用同樣既能夠是強類型,也能夠是弱類型,並且能夠從一個進程傳給其它進程,讓你們都能訪問同一Server,就象將一個對象或引用賦值給另外一個引用同樣。Binder模糊了進程邊界,淡化了進程間通訊過程,整個系統彷彿運行於同一個面向對象的程序之中。形形色色的Binder對象以及星羅棋佈的引用彷彿粘接各個應用程序的膠水,這也是Binder在英文裏的原意。
Binder基於C/S的結構下,定義了4個角色:Server、Client、ServerManager、Binder驅動,其中前三者是在用戶空間的,也就是彼此之間沒法直接進行交互,Binder驅動是屬於內核空間的,屬於整個通訊的核心,雖然叫驅動,可是實際上和硬件沒有太大關係,只是實現的方式和驅動差很少,驅動負責進程之間Binder通訊的創建,Binder在進程之間的傳遞,Binder引用計數管理,數據包在進程之間的傳遞和交互等一系列底層支持。
ServerManager的做用?
咱們知道ServerManager也是屬於用戶空間的一個進程,主要做用就是做爲Server和client的橋樑,client能夠ServerManager拿到Server中Binder實體的引用,這麼說可能有點模糊,舉個簡單的例子,咱們訪問,www.baidu.com,百度首頁頁面就顯示出來了,首先咱們知道,這個頁面確定是發佈在百度某個服務器上的,DNS經過你這個地址,解析出對應的ip地址,再去訪問對應的頁面,而後再把數據返回給客戶端,完成交互。這個和Binder的C/S很是相似,這裏的DNS就是對應的ServerManager,首先,Server中的Binder實體對象,將本身的引用(也就是ip地址)註冊到ServerManager,client經過特定的key(也就是百度這個網址)和這個引用進行綁定,ServerManager內部本身維護一個相似MAP的表來一一對應,經過這個key就能夠向ServerManager拿到Server中Binder的引用,對應到Android開發中,咱們知道不少系統服務都是經過Binder去和AMS進行交互的,好比獲取音量服務:
AudioManager am = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
細心的朋友應該發現ServerManager和Server也是兩個不一樣的進程呀,Server要向ServerManager去註冊不是也要涉及到進程間的通訊嗎,當前實現進程間通訊又要用到進程間的通訊,你這不是扯犢子嗎....莫急莫急,Binder的巧妙之處在於,當ServerManager做爲Serve端的時候,它提供的Binder比較特殊,它沒有名字也不須要註冊,當一個進程使用BINDER_SET_CONTEXT_MGR命令將本身註冊成SMgr時Binder驅動會自動爲它建立Binder實體,這個Binder的引用在全部Client中都固定爲0而無須經過其它手段得到。也就是說,一個Server若要向ServerManager註冊本身Binder就必需經過0這個引用號和ServerManager的Binder通訊,有朋友又要問了,server和client屬於兩個不一樣的進程,client怎麼能拿到server中對象,不妨先看看下面的交互圖
從上圖很清晰的能夠看出來整個的交互過程,本來從SM中拿到binder的引用,經過Binder驅動層的處理以後,返回給了client一個代理對象,實際上若是client和server處於同一個進程,返回的就是當前binder對象,若是client和server不處於同一個進程,返回給client的就是一個代理對象,這一點,有興趣能夠看下源碼。
Binder本質上只是提供了一種通訊的方式,和咱們具體要實現的內容沒有關係,爲了實現這個服務,咱們須要定義一些接口,讓client可以遠程調用服務,由於是跨進程,這時候就要設計到代理模式,以接口函數位基準,client和server去實現接口函數,Server是服務真正的實現,client做爲一個遠程的調用。
上面說了那麼多,你們夥也看累了,下面經過代碼的形式讓你們對Binder加深點理解,平常開發中,涉及到進程間通訊的話,咱們首先想到的可能就是AIDL,但不知道有沒有和我感受同樣的朋友。。第一次寫AIDL是矇蔽的,經過.aidl文件,編譯器自動生成代碼,生成一個java文件,裏面又有Stub類,裏面還有Proxy類,徹底不理解裏面的機制,確實不便於咱們理解學習,爲了加深理解,咱們拋棄aidl,手寫一個通訊代碼。
首先咱們要定義一個接口服務,也就是上述的服務端要具有的能力來提供給客戶端,定義一個接口繼承IInterface,表明了服務端的能力
public interface PersonManger extends IInterface {
void addPerson(Person mPerson);
List<Person> getPersonList();
}
複製代碼
接下來咱們就要定義一個Server中的Binder實體對象了,首先確定要繼承Binder,其次須要實現上面定義好的服務接口
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驅動傳來的IBinder對象,經過queryLocalInterface方法,查找本地Binder對象,若是返回的就是PersonManger,說明client和server處於同一個進程,直接返回,若是不是,返回給一個代理對象。
固然做爲代理對象,也是須要實現服務接口
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;
}
}
複製代碼
這裏的代理對象實質就是client最終拿到的代理服務,經過這個就能夠和Server進行通訊了,首先經過Parcel將數據序列化,而後調用 remote.transact()將方法code,和data傳輸過去,對應的會回調在在Server中的onTransact()中
而後是咱們的Server進程,onBind方法返回mStub對象,也就是Server中的Binder實體對象
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;
}
};
}
複製代碼
最終咱們在客戶端進程,bindService傳入一個ServiceConnection對象,在與服務端創建鏈接時,經過咱們定義好的BinderObj的asInterface方法返回一個代理對象,再調用方法進行交互
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;
}
};
}
複製代碼
這樣的話,一次完成的進程間的交互就完成了~是否是感受沒有想象中那麼難,最後建議你們在不借助 AIDL 的狀況下手寫實現 Client 和 Server 進程的通訊,加深對 Binder 通訊過程的理解。
本文在寫做過程當中參考了蠻多的文章和源碼,感謝大佬們的無私奉獻,溜了溜了~
參考文章