基礎知識
Android進程間的通訊沒有沿用Linux的原有的通訊模式,而是採用新的通訊模式Binder.
先了解下Linux原有的通訊方式:Linux進程間的通信方式和原理
進程通信的應用場景一般包含數據/資源共享,數據傳輸,通知事件,進程控制。主動被動都有了。
Linux原有的幾種通信方式
- pipe管道
- 消息隊列(Message)
- 信號量(Semaphore)
- 共享內存(Share Memory)
- 信號(Signal)
- 跟蹤(Trace)
- 還有支持支持C/S的Socket
Android爲什麼採用Binder?
Android沒有使用Linux原生的通信機制,而是使用Binder機制。
Binder機制是採用OpenBinder演化而來,採用C/S的通信模式。
在Android中使用它的原因如下
- socket效率低,開銷大。linux目前只有socket支持C/S
- 管道和消息隊列效率低。管道和消息採用存儲轉發方式,所以至少需要拷貝2次數據,效率低
- 共享內存控制機制複雜:共享內存雖然在傳輸時沒有拷貝數據,但其控制機制複雜(比如跨進程通信時,需獲取對方進程的pid,得多種機制協同操作)。
- Binder安全性更高
- Linux的IPC機制在本身的實現中,並沒有安全措施,得依賴上層協議來進行安全控制。
- Binder機制的UID/PID是由Binder機制本身在內核空間添加身份標識,安全性高;並且Binder可以建立私有通道,這是linux的通信機制所無法實現的(Linux訪問的接入點是開放的)
各種IPC方式數據拷貝次數對比
IPC方式 | 數據拷貝次數 |
---|---|
共享內存 | 0 |
Binder | 1 |
Socket/管道/消息隊列 | 2 |
總之,基於以上原因,Linux上原生機制無法滿足手機的要求,Android需要建立一套新的IPC機制來滿足系統對通信方式的傳輸性能和安全性要求,這就出現了Binder
Binder是什麼
Binder是Android系統中進程間通訊(IPC)的一種方式,像Intent, Messenger, ContentProvider底層都是Binder實現
- Binder指的是一種通信機制。AIDL使用Binder進行通信,指的就是Binder這種IPC機制。
對於Server進程來說,Binder指的是Binder本地對象。
- Server端一般會實例一個Binder實現具體的接口,即Server能夠提供的能力(注意因爲Stub是abstract不能直接實例化,實例化的父類Binder對象)
在onBind時將實例傳遞給Client
``` public class BookManagerService extends Service { private static final String TAG = BookManagerService.class.getSimpleName(); // book list private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>(); // new book listener RemoteCallbackList used for multi-process listener private RemoteCallbackList<INewBookListener> mListenerList = new RemoteCallbackList<INewBookListener>(); private Binder mBinder = new IBookManager.Stub() { @Override public List<Book> getBookList() throws RemoteException { SystemClock.sleep(2000); return mBookList; } @Override public void addBook(Book book) throws RemoteException { mBookList.add(book); onNewBookArrived(book); } ...... } @Override public IBinder onBind(Intent intent) { if (!verifyPermission()) { return null; } return mBinder; } } ```
對於Client來說,Binder指的是Binder代理對象,只是Binder本地對象的一個遠程代理
- Client通過asInterface方法獲取Proxy代理或通過asBinder方法獲取Binder實例
- 對這個Binder代理對象的操作,會通過驅動最終轉發到Binder本地對象上去完成,即調用Service的Binder實例的方法。
- 對於一個擁有Binder對象的使用者而言,它無須關心這是一個Binder代理對象還是Binder本地對象。對於代理對象的操作和對本地對象的操作對它來說沒有區別。因爲實現了同一個接口
public class BookManagerActivity extends AppCompatActivity { private static final String TAG = BookManagerActivity.class.getSimpleName(); private IBookManager mRemoteBookManager; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_book_manager); initView(); // bind service : after 5.0 must follow // https://developer.android.com/google/play/billing/billing_integrate.html#billing-requests Intent intent = new Intent(this, BookManagerService.class); intent.setPackage(getPackageName()); bindService(intent, mServiceConn, Context.BIND_AUTO_CREATE); } private ServiceConnection mServiceConn = new ServiceConnection() { public void onServiceConnected(ComponentName className, IBinder service) { mRemoteBookManager = IBookManager.Stub.asInterface(service); if (mRemoteBookManager != null) try { mRemoteBookManager.asBinder().linkToDeath(mDeathRecipient, 0); mRemoteBookManager.registerListener(mNewBookListener); } catch (RemoteException e) { e.printStackTrace(); } Log.d(TAG, "bind service"); } public void onServiceDisconnected(ComponentName className) { mRemoteBookManager = null; Log.d(TAG, "onServiceDisconnected, tid = " + Thread.currentThread().getName()); } }; public void addBook(View v) { if (mRemoteBookManager == null) { Log.w(TAG, "mRemoteBookManager is null"); return; } bookThreadPool.execute(new Runnable() { @Override public void run() { try { int bookId = mRemoteBookManager.getBookSize() + 1; Book newBook = new Book(bookId, "new-" + bookId); mRemoteBookManager.addBook(newBook); Log.d(TAG, "add book:" + newBook); } catch (RemoteException e) { e.printStackTrace(); } } }); } }
對於傳輸過程而言,Binder是可以進行跨進程傳遞的對象;Binder驅動會對具有跨進程傳遞能力的對象做特殊處理:自動完成代理對象和本地對象的轉換
代碼片一
interface IBookManager {
// get all book list
List<Book> getBookList();
// add one book
void addBook(in Book book);
// get book list size
int getBookSize();
// register and unregister new book listener
void registerListener(INewBookListener listener);
void unregisterListener(INewBookListener listener);
}
編譯器生成的IBookManager
全部Code在這裏Github IPC
Binder架構
Binder架構圖(來自網上)
詳細架構圖(來自網上)
- Binder 通信採用 C/S 架構:包含 Client、 Server、 ServiceManager 以及 Binder 驅動。
- ServiceManager 用於管理系統中的各種服務。無論是註冊服務和獲取服務的過程都需要ServiceManager(Native C++層的ServiceManager不是framework java層的ServiceManager
- Binder 在 framework 層進行了封裝,通過 JNI 技術調用 Native(C/C++)層的 Binder 架構。
- Binder 在 Native 層以 ioctl 的方式與 Binder 驅動通訊
Binder機制
下圖是Binder機制是如何跨進程。(來自網絡)
每個Android的進程,只能運行在自己進程所擁有的虛擬地址空間。對應一個4GB的虛擬地址空間,其中3GB是用戶空間,1GB是內核空間(大小可以通過參數配置調整)。對於用戶空間,不同進程之間彼此是不能共享的,而內核空間卻是可共享的。Client進程向Server進程通信,恰恰是利用進程間可共享的內核內存空間來完成底層通信工作。Client端與Server端進程往往採用ioctl等方法跟內核空間的驅動進行交互。
ServiceManager是整個Binder通信機制的大管家,是Android進程間通信機制Binder的守護進程。當Service Manager啓動之後,Client端和Server端通信時都需要先獲取Service Manager接口,才能開始通信服務。ServiceManager是由init進程通過解析init.rc文件而創建的。
- 註冊服務(addService):Server進程要先註冊Service到ServiceManager。該過程:Server是客戶端,ServiceManager是服務端。
- 獲取服務(getService):Client進程使用某個Service前,須先向ServiceManager中獲取相應的Service。該過程:Client是客戶端,ServiceManager是服務端。
- 使用服務:Client根據得到的Service信息建立與Service所在的Server進程通信的通路,然後就可以直接與Service交互。該過程:client是客戶端,server是服務端。
Binder通信的基本流程
一次完整Binder通信圖(來自網上)
深入理解Java層的Binder
- IBinder是一個接口,它代表了一種跨進程傳輸的能力
- 只要實現了這個接口,就能將這個對象進行跨進程傳遞。這是驅動底層支持的
- 在跨進程數據流經驅動的時候,驅動會識別IBinder類型的數據,從而自動完成不同進程Binder本地對象以及Binder代理對象的轉換
- IBinder負責數據傳輸,IInterface代表的就是遠程server對象具有什麼能力。就是aidl裏面的接口
- Java層的Binder類,代表的其實就是Binder本地對象。
- BinderProxy類是Binder類的一個內部類,它代表遠程進程的Binder對象的本地代理;這兩個類都繼承自IBinder, 因而都具有跨進程傳輸的能力;
- 實際上,在跨越進程的時候,Binder驅動會自動完成這兩個對象的轉換。
- 在使用AIDL的時候,編譯工具會給我們生成一個Stub的靜態內部類;
- 繼承了Binder, 說明它是一個Binder本地對象
- 它實現了IInterface接口,表明它具有遠程Server承諾給Client的能力;
- Stub是一個抽象類,具體的IInterface的相關實現需要我們手動完成,這裏使用了策略模式。
看這篇的深入理解Java層的Binder
Binder源碼解析
framework層的源碼分析可以看這裏
Reference
一篇文章瞭解相見恨晚的 Android Binder 進程間通訊機制
老羅的 Android進程間通信(IPC)機制Binder簡要介紹和學習計劃 系列