想寫篇關於Binder的文章,可對其一無所知,無從下手。在閱讀了大量的優秀文章後,心驚膽戰的提筆,不怕文章被貽笑大方,怕的是誤人子弟!望各位大佬抽空閱讀本文的同時,可以對文章的知識點持懷疑態度,共同探討,共同進步!java
平常開發中,經過Intent攜帶數據跳轉Activity時,數據一般要經過實現Serializable或Parcelable接口,才能在被Intent所攜帶,而Serializable接口和Parcelabel接口主要是完成對象的序列化過程。將對象持久化到設備上或者網絡傳輸一樣也須要序列化。android
Serializable接口是Java所提供的,爲對象提供標準的序列化和反序列化操做。一般一個對象實現Serializable接口,該對象就具備被序列化和反序列化的能力,並且幾乎全部工做有系統自動完成。Serializable接口內serialVersionID可指定也能夠不指定,其做用是用來判斷序列化前和反序列化的類版本是否發生變化。該變量若是值不一致,表示類中某些屬性或者方法發生了更改,反序列化則出問題。(靜態成員變量和transient關鍵字標記的成員不參與序列化過程)git
Parcelable 接口是Android所提供的,其實現相對來講比價複雜。實現該接口的類的對象就能夠在Intent和Binder進行傳遞。github
Serializable是Java提供的接口,使用簡單,但序列化與反序列化須要大量的IO操做,因此開銷比較大。Parcelable是Android提供的序列化方法,使用麻煩當效率高。在Android開發中,將對象序列化到設備或者序列化後經過網絡傳輸建議使用Serializable接口,其餘狀況建議是用Parcelable接口,尤爲在內存的序列化上。例如Intent和Binder傳輸數據。編程
在Java層,想利用Binder進行誇進程的通訊,那就得經過AIDL(Android 接口定義語言)了,AIDL是客戶端與服務使用進程間通訊 (IPC) 進行相互通訊時都承認的編程接口,只有容許不一樣應用的客戶端用 IPC 方式訪問服務,而且想要在服務中處理多線程時,纔有必要使用 AIDL,若是是在單應用(單進程),建議使用Messager。安全
自定義的Parcelable對象和AIDL接口必須顯示導入到AIDL文件中。bash
數據的走向markdown
Parcelable對象和AIDL接口在使用前必須標明數據的走向:網絡
示例:多線程
void addUser(inout User user);
複製代碼
定義一個實現了Parcelable 接口,做爲客戶端和服務端傳輸的數據對象。
public class User implements Parcelable { private String username; private String address; public User() { } public User(String username, String address) { this.username = username; this.address = address; } User(Parcel in) { readFromParcel(in); } //系統默認生成,反序列化過程,咱們只須要要構造方法讀取相關值就能夠 public static final Creator<User> CREATOR = new Creator<User>() { @Override public User createFromParcel(Parcel in) { return new User(in); } @Override public User[] newArray(int size) { return new User[size]; } }; //系統默認生成,內容描述功能,幾乎全部狀況下都返回0, //僅僅當前存在文件描述符,才返回1 @Override public int describeContents() { return 0; } //序列化過程,經過一系列的write將值寫到Parcel 對象 @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(username); dest.writeString(address); } @Override public String toString() { return username+":"+address; } public void readFromParcel(Parcel in){ username=in.readString(); address=in.readString(); } } 複製代碼
經過下面方法,創建一個UserManger.aidl文件,表示服務端能爲客戶端提供什麼樣的服務。
下面代碼經過創建UserManager.aidl文件,爲客戶端提供addUser
和
getUser
的能力。UserManager能夠理解爲,服務端和客戶端的共同約定,二者能進行怎麼樣的交互。
package com.gitcode.server;
// 在這裏要導入傳遞對象的類型,例如User
import com.gitcode.server.User;
interface UserManager {
void addUser(inout User user);
User getUser(int index);
}
複製代碼
定義UserManager.aidl文件後,系統默認會生成UserManager.java文件。
UserManager.java的代碼以下,爲了減小篇幅,去掉了一些實現。
public interface UserManager extends android.os.IInterface { public static abstract class Stub extends android.os.Binder implements com.gitcode.server.UserManager { private static final String DESCRIPTOR = "com.gitcode.server.UserManager"; public Stub() { this.attachInterface(this, DESCRIPTOR); } public static com.gitcode.server.UserManager asInterface(android.os.IBinder obj) { if ((obj == null)) { return null; } android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR); if (((iin != null) && (iin instanceof com.gitcode.server.UserManager))) { return ((com.gitcode.server.UserManager) iin); } return new 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 { ...... } private static class Proxy implements com.gitcode.server.UserManager { private android.os.IBinder mRemote; Proxy(android.os.IBinder remote) { mRemote = remote; } @Override public android.os.IBinder asBinder() { return mRemote; } public String getInterfaceDescriptor() { return DESCRIPTOR; } @Override public void addUser(com.gitcode.server.User user) throws android.os.RemoteException { ...... } @Override public com.gitcode.server.User getUser(int index) throws android.os.RemoteException { ..... } } static final int TRANSACTION_addUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0); static final int TRANSACTION_getUser = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1); } public void addUser(com.gitcode.server.User user) throws android.os.RemoteException; public com.gitcode.server.User getUser(int index) throws android.os.RemoteException; } 複製代碼
從上文可知,UserManager自己是一個接口,並繼承IInterface接口。UserManager.java聲明瞭addUser
和getUser
,和在UserManager.aidl的聲明是一致的。同時聲明兩個整型TRANSACTION_addUser
和TRANSACTION_getUser
,用於在transact()
方法中標識調用服務端哪一個方法。若是服務端和客戶端在不一樣進程,方法調用會走transact()
方法,邏輯由Stub 和Proxy 內部類完成。
內部類Stub的一些概念和方法含義:
DESCRIPTOR
Binder的惟一標識,通常用當前的類名全名標識。
asInterface(IBinder obj)
將服務端的Binder對象轉換成客戶端的AIDL接口類型的對象,若是客戶端和服務端同一進程,直接返回Stub對象自己,不在同一進程,則返回由系統封裝的Stub.proxy對象。
asBinder
返回當前Binder對象
onTransact(int code, Parcel data, Parcel reply, int flags)
運行在服務端Binder線程池,當客戶端跨進程發起請求後,系統封裝後交由此方法來處理。code表示調用服務端什麼方法,上文聲明的整型。data表示客戶端傳遞過來的數據,reply爲服務端對客戶端的回覆。
內部代理類 Poxy,表示客戶端遠程能對服務端進行的操做。
addUser運行在客戶端,當客戶端遠程調用時,
在相同目錄下建立User.aidl,能夠直接複製UserManager.aidl,內容修改以下。
package com.gitcode.server;
parcelable User;
複製代碼
在服務端中,服務通常以Service體現,定義UserServcie,繼承Service。
public class UserService extends Service { private static final String TAG = "Server"; private List<User> list = new ArrayList<>(); @Override public void onCreate() { super.onCreate(); list.add(new User("GitCode", "深圳")); } @Override public IBinder onBind(Intent intent) { Log.i(TAG,"on Bind"); return stub; } private UserManager.Stub stub = new UserManager.Stub() { @Override public void addUser(User user) throws RemoteException { list.add(user); Log.i(TAG,"add user:"+user); } @Override public User getUser(int index) throws RemoteException { Log.i(TAG,"get user,index:"+index); return list.size() > index && index >= 0 ? list.get(index) : null; } }; } 複製代碼
在AndroidManifest.xml文件聲明Service,以兩個組件造成單獨的app來體現兩個進程,經過AIDL進行數據交互。在客戶端經過bindService()
來啓動該服務。
<service android:name="com.gitcode.server.UserService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.gitcode.server.userservice"/> <category android:name="android.intent.category.DEFAULT"/> </intent-filter> </service> 複製代碼
客戶端主要是經過共同的約定(UserManger.aidl)向服務端進行請求,服務端響應客戶端的請求。爲了提升效率和減小出錯,經過拷貝來實現客戶端的AIDL文件。將服務端的aidl整個文件拷貝到客戶端的main目錄下,不作任何修改。
在客戶端創建與服務端User類同包的目錄,並將User類拷貝過來,不作任何修改。 在Activity中綁定服務端的Service,綁定成功後進行數據交互。public class MainActivity extends AppCompatActivity { private static final String TAG = "Client"; private UserManager mUserManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); toBindService(); } private void toBindService() { Intent intent = new Intent("com.gitcode.server.userservice"); intent.setPackage("com.gitcode.server"); bindService(intent, connection, Context.BIND_AUTO_CREATE); } private ServiceConnection connection = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mUserManager = UserManager.Stub.asInterface(service); try { User user = mUserManager.getUser(0); Log.e(TAG, user.toString()); mUserManager.addUser(new User("張三","北京")); } catch (RemoteException e) { e.printStackTrace(); } } @Override public void onServiceDisconnected(ComponentName name) { } }; } 複製代碼
運行效果:
客戶端:
服務端:客戶端調用服務的方法,被調用的方法運行在服務端的的Binder線程池,同時客戶端會被掛起,若是服務端方法執行耗時操做,就會致使客戶端ANR,因此不要在客戶端主線程訪問遠程服務方法。同時服務端不該該本身新建新建線程運行服務方法,由於方法會交由線程池處理,同時對數據也要作好併發訪問處理。
AIDL能夠說爲應用層開發提供了封裝,不用過多的瞭解Binder的機制,經過生成的UserManager.java,初步能夠了解Binder的IPC機制。使用AIDL在進程之間進行數據通訊,更注重的是細節和業務的實現。
Binder進制是Android系統提供的一種IPC機制。因爲Android是基於Linux內核,所以,除了Binder之外,還有其餘的IPC機制,例如Socket,共享內存,管道和消息隊列等。之全部不使用原有的 IPC機制,是由於使用Binder機制,能從性能、穩定性、安全性帶來更好的效果。例如,Socket是一套通用的接口,傳輸速率低下,適合網絡傳輸這種狀況,而管道和消息隊列須要數據的兩次拷貝,共享內容難以管控等。而Binder對數據只須要一次拷貝,使用C/S架構,職責明確,容易維護和使用。
經過下圖能夠了解到,Binder機制經過內存映射實現跨進程通訊,Binder在IPC機制只是做爲一個數據的載體,當進程A向虛擬內存空間中寫入數據,數據會被實時反饋到進程B的虛擬內存空間。整個發送數據的過程,只從用戶空間拷貝一次到虛擬內存空間。
在Binder機制中,主要涉及到Client、Server、ServiceManger三個端,三者經過Binder進行跨進程通訊,支持着Android這個大網絡。它們的關係以下圖。 ServerServer進程須要註冊一些Service到ServiceManger中,以對外告知其可提供的服務。例如上文AIDL中,會註冊UserService,併爲Client提供添加User和獲取User的操做。註冊的過程,Server進程就是客戶端,而ServiceManger就是服務端。
Client
對Sever進程進行業務邏輯操做。經過Service的名稱在ServiceManger查找對應的Service。
ServiceManager
ServiceManger集中管理系統內的全部Service,服務經過註冊Service到ServiceManger的查找表中,當Client根據Service名稱請求ServiceManger在查找表中查詢對應的Service。
圖表示三者的C/S架構,例如Client查詢向ServiceManger查詢Service時,Client就是客戶端,而ServiceManger就是服務端。而虛線則表示二者之間經過Binder進行進程間的通訊,所以經過了解一條虛線的流程,就能夠知道Binder的機制。
咱們常經過getSystemService()
函數來獲取系統的相關服務,例如ActivityManagerService
,那麼這些Service從哪裏來呢?就是有些服務進程向ServiceManager註冊本身的一些Service,表示本身能對client提供什麼樣的服務。經過了解Service的註冊過程,來學習Binder機制的工做原理。
每一個進程經過單例模式建立了惟一的ProcessState對象,在其構造器中,經過open_driver()
方法打開了/dev/binder設備,至關於Server進程打開了與內核的Binder驅動交互的通道,並設置最大支持線程數爲15。binder設備是Android在內核中爲完成進程間通訊而專門設置的一個虛擬設備。
BpBinder與BBinder都是Android與Binder通訊相關的表明,二者一一對應,都從IBinder派生而來。若是說BpBinder表明客戶端,那麼BBinder就表明服務端,一個BpBinder經過handler標識與對應的BBinder進行交互。在Binder系統,handler標識爲0表明着ServiceManger所對應的BBinder。BpBinder與BBinder並無直接操做ProcessState打開的binder設備。
二者繼承至IServiceManger,與業務邏輯相關,能夠說將業務層的邏輯架構到Binder機制上。BnserviceManger從IServiceManger BBinder派生而來,可直接參與Binder的通訊,而BpServiceManger經過mRemote指向BpBinder。
經過上文三小節,BpServiceManger對象實現對IServiceManger的業務函數,又有BpBinder做爲通訊表明,下面分析一下注冊的過程。
將字符串名字和Service對象做爲參數傳到BpServiceManger對象的addService()
函數,該方法將參數數據打包後傳遞給BpBidner的transact()
函數。業務層的邏輯到此就結束,主要做用是將請求信息打包交給通訊層去處理。
在BpBinder的transact()
函數調用了IPCThreadState對象的transact()
函數,因此說BpBinder自己沒有參與Binder設備的交互。每一個線程都有一個IPCThreadState對象,其擁有一個mOut、mIn的緩衝區,mOut用來存儲轉發Binder設備的數據,而mIn用來接收Binder設備的數據。經過ioctl
方式與Binder設備進行交互。
經過上文Service的註冊過程,分析了Binder的機制。Binder只是通訊機制,業務能夠基於Binder機制,也能夠基於其餘IPC方式的機制,也就是上文爲啥有BpServiceManger和BpBinder。Binder之因此複雜,是Android經過層層的封裝,巧妙的將業務與通訊融合在一塊兒。主要仍是設計理想很牛逼。
經過1小節的分析,是否應該也有一個類繼承自BnServiceManger來處理遠方請求呢?
很惋惜的是在服務端並無BnServiceManger子類來響應遠程客戶端的請求,而是交給了ServiceManger來處理。
ServiceManger經過binder_open函數打開binder設備,並映射內存。經過handler等於0標識本身,讓本身成爲管理中心,全部service向ServiceManger註冊時,都是經過handle標識爲的0的BpBinder找到ServiceManger對應的BBinder,ServiceManager會保存要註冊的Service的相關信息,方便Client查找。並非全部的Service均可以在ServiceManger註冊,若是Server進程的權限不夠root或system,那麼須要在allowed添加相應的項。
Client想要使用Server進程提供的Service,又該進行哪些步驟呢?
Client想要獲得某個Service的信息,就得與ServiceManager打交道,經過調用getService()方法來獲取對應Service信息。Client經過服務名稱向ServiceManger查詢對應的Service。若是Service未註冊,則循環等待直到該Service註冊;若是已註冊,則會對應封裝了一個能與遠程Service通訊的BpBinder的BpXXXService,經過該Service,Client客戶調用相關業務邏輯函數。
Client調用的業務函數,莫非就是將請求參數打包發送給Binder驅動,BpBinder經過handler的值找到對應端的Service來處理。
在1.4小節中,說到IPCThreadState對象,在其executeCommand函數中,經過調用實現了BnServiceXXX的對象onTransact函數,直接定位到業務層。這就是在AIDL中,爲何在onTransact()函數中處理響應數據。
經過對Binder機制的學習,瞭解Android是如何經過層層封裝將Binder機制集成要應用程序,對Binder機制有一個較深刻的理解。能夠經過第Java層AIDL的使用,加深對Binder機制的理解。
我的水平有限,有誤請幫忙勘正,謝謝大佬。喜歡就幫忙點個讚唄。
參考資料: