Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android-----我眼中的Binder

android-----我眼中的Binder

編輯:關於Android編程

Binder作為進程間通信方式(IPC)的一種,算Android中比較難理解的部分了,今天計劃以自己所認識的framework層的Binder原理來做個總結,好了,我們開始吧!

Android中利用Binder通信,首先肯定需要獲得Binder對象了,但是系統服務和我們自定義服務Binder對象的獲取方式是不一樣的,原因就在於系統服務是在系統啟動的時候被注冊到ServiceManegr的,我們只需要通過ServiceManager.getService(String name)傳入想要獲得的系統服務的名字就可以獲得其對應的Binder對象進行進程間通信了,系統已經將該Service所提供的服務內容全部寫好了;但是對於我們自定義的服務想要實現進程間通信的話,Client端和Server端都是我們自己寫的,所以Android提供了AIDL來幫你簡化這個過程,而Server端的實現一般是借助於Service來完成的,下面我們分別從系統服務和自定義服務兩個方面來進行分析;

先來看系統服務:

首先從系統剛剛啟動說起,init進程會啟動一個叫ServiceManager的進程,該進程啟動之後會做三件事:(1)通過open打開設備文件/dev/binder,將該文件中的內容通過mmap映射到本進程空間中;(2)通過IO控制命令BINDER_SET_CONTEXT_MGR將當前進程注冊到Binder驅動中,Binder驅動便會為他在內核空間創建一個稱為binder_context_mgr_node的節點出來,這個節點也就是ServiceManager在內核空間的Binder實體了,並且將他的句柄設置為0,他的創建是在系統啟動的時候,這個節點在Binder驅動中是唯一的,所以也就造成了ServiceManager區別於其他Server了,但他仍然是運行在用戶空間的;(3)ServiceManager啟動之後就會無限循環等待Server和Client之間的進程間通信請求了;接著Zygote進程會孵化出一個子進程SystemService,我們的大部分系統服務比如ActivityManagerService、WindowManagerService、MediaPlayService等都是該進程內的一個線程,這些服務會通過調用ServiceManager.addService方法添加到ServiceManager中的服務列表svclist中,這樣我們的系統服務就已經都注冊到了ServiceManager裡面了;

上面講了ServiceManager,他是隨著系統的啟動而創建的,它不同於普通的Server,甚至可以理解為是普通Server的Server,系統會將系統所要提供的服務注冊到它的服務列表裡面,服務列表中存儲的便是服務名字+服務在Binder驅動中的句柄,這裡的句柄可以理解為同一進程內部的引用,只不過現在是跨進程通信換了個名字而已,如果我們的應用想要用到某個系統服務的話,可以通過傳入服務名字調用ServiceManager.getService得到該服務對應的Binder對象的方式進行獲取,接著通過這個Binder對象進行相應操作即可,具體這就是ContentProvider為我們封裝的內容了;

Binder是通過客戶端/服務端模式實現的,要想明白Binder的實現原理,我們需要明白幾個概念之間的關系:Server/Client/ServiceManager/Binder驅動,他們四個是Binder架構的核心,尤其是ServiceManager和Binder驅動,因為我們的應用程序安裝到系統上的時候,都會被分配一個唯一的UID用戶ID,他是運行在獨立進程中的,Linux基於用戶安全的考慮是不允許我們直接訪問系統上服務的,因為他們分屬於不用的進程,進程間的數據是不能共享的,我們要想訪問這些系統服務就涉及到了進程間通信了,那麼勢必就需要內核空間的參與了,所以用到了Binder驅動,它有點類似於橋的作用;那麼平常我們是怎麼做到訪問系統服務的呢?比如媒體資源,是通過ContentProvider,他的底層實現就是Binder,只不過被封裝了而已;

我們可以通過一個例子來說明他們四者之間的關系,假如你想問你同學借點錢,那麼你現在就是Client端,你同學就是Server端,但是你兩是異地的,不能直接見面,怎麼辦呢,打電話呗,首先你先從通訊錄裡面找到你同學的電話,這裡的通訊錄就是ServiceManager了,然後打過去借就好了,那麼電信運營商就是Binder驅動了,沒有他你兩根本聯系不起來;

講完了所有涉及到的概念之後,就該看看是怎麼通信的了,也就是ServiceManager.addService和ServiceManager.getService到底做了些什麼事了?

首先來說說ServiceManager.addService,也就是我們的Server是怎麼和Binder驅動通信的:

ServiceManager.addService(String name, IBinder service):傳入的內容是Server的名字和該Server在Binder驅動中對應的對象;

(1):Server首先將自己作為對象,並且附上一個句柄為0的值(用於訪問ServiceManager),將這些內容封裝成一個數據包,open有關Binder的設備文件/dev/binder,將封裝好的該數據包發送給Binder驅動;

(2):Binder驅動在收到這個數據包之後,發現裡面存在一個Server對象,首先會在Binder驅動自己裡面新建該Server對應的Binder實體,並賦予一個大於0的句柄,同時會將該句柄也加入到數據包中,接著將該數據包發送到句柄為0對應的對象上面,也就是ServiceManager上面了;

(3):ServiceManager收到轉發給自己的數據包之後,會查看其服務列表svlist中是否已經存在當前Server名字的服務,不存在的話,會將當前服務+當前服務對應於Binder驅動中的句柄加入到列表中;

這樣,系統服務就注冊到ServiceManager中了;

接下來便是ServiceManager.getService部分了,也就是Client是怎麼和Binder驅動進行通信,返回對應請求的Binder對象,也就是該Server在Binder驅動中的句柄的:

ServiceManager.getService(String name):傳入的參數是將要請求的服務的名字

(1):Client首先會將要獲取的服務的名字以及一個句柄為0的值(為了訪問ServiceManager)封裝成一個數據包,open有關Binder的設備文件/dev/binder,將該數據包發送給Binder驅動;

(2):Binder驅動在收到數據包之後發現裡面有句柄為0的信息,就將該數據包轉發給了ServiceManager來進行處理了;

(3):ServiceManager在收到數據包之後根據服務的名字查看自己的服務列表svclist,找到之後會將其對應的在Binder驅動中的句柄信息也封裝成一個數據包;

(4):該數據包也會通過Binder驅動被發送給Client端;

(5):Client端在收到數據包之後,就得到了自己所請求的服務在Binder驅動中的句柄,他會利用這個句柄信息在自己本地創建一個遠程Server的代理,以後Client發消息都是發給這個代理的,隨後的通信便變成了代理通過Binder驅動與真正的Server進行交互了,以此完成跨進程間的通信;

這樣系統服務是怎麼注冊到ServiceManager裡面以及我們怎麼獲得這些服務對應於Binder驅動的句柄也就是Binder對象的過程講解就已經結束了,接下來便是我們自定義服務是怎麼通過Binder進行進程間通信的呢?

自定義服務:

這裡就要用到Android為我們自定義進程間通信所提供的AIDL文件了,沒有這個文件,你完全也可以實現跨進程通信,但是序列化,反序列化數據的封裝都將需要你自己來實現,有了AIDL之後這個過程會顯的比較簡單,你並不需要關心序列化反序列化的順序問題,只需要將Server進程想要提供給Client進程訪問的方法定義在一個.aidl文件中即可,我們在此將他命名為Ixxx.aidl,那麼系統將會為該AIDL文件自動生成對應的Ixxx.java文件;

簡單說說Ixxx.java的類結構,將是下面這樣的偽代碼形式:

 

public interface Ixxx extends IInterface
{
	public static abstarct class Stub extends Binder implements Ixxx
	{
		public static Ixxx asInterface(Binder binder){}
		public Binder asBinder(){}
		public boolean onTransact(int code,Parcel data,Parcel reply,int flags){}
		private static class Proxy implements Ixxx
		{
			public Binder asBinder(){}
			//Ixxx接口中的一些實現方法,當然這些實現並不是真正的邏輯實現,而只是通過transcat進行的一些進程切換操作
		}
	}
}

 

可以看到Ixxx.java中存在一個Stub靜態抽象類和Prxoy靜態類,最關鍵的方法就是Stub類中的asInterface了,他會根據我們傳入的Binder對象來判斷是跨進程通信還是進程內部通信,如果是進程內部通信,該方法返回的將是Ixxx.Stub對象;如果是跨進程
通信返回的將是Ixxx.Stub.Proxy對象,這個代理對象中將的方法會通過調用transact方法來進行內核態的切換;

下面我以文字的方式簡述下我們自定義服務實現跨進程通信的原理:

(1):首先,我們需要創建一個AIDL文件,將其命名為Ixxx.aidl,裡面定義了服務端進程想要提供給客戶端進程的方法列表,系統會為我們生成一個Ixxx.java文件;

(2):我們的自定義服務端主要是通過service來實現的,在service裡面實現具體提供給客戶端的方法的操作代碼,並且通過onBind方法返回此服務端對應的Binder對象,而後我們的客戶端通過bindService綁定服務端的時候就可以獲得這個服務端的Binder對象了;

(3):在客戶端獲得Binder對象之後會調用Ixxx.Stub的asInterface方法將Binder對象傳入,獲得Ixxx對象,在這裡就將跨進程和通進程通信分開了,如果是跨進程通信的話asInterface返回的是Ixxx.Stub.Proxy代理對象,那麼以後客戶端調用服務端的方法實際上是首先調用的Ixxx.Stub.Proxy代理對象裡面對應於服務端的方法,這個代理對象的方法會通過transact陷入內核態來進行實際上的進程間通信調用服務端的onTransact方法,在onTransact方法中會根據標志調用不同的服務端方法;如果是同進程通信的話,asInterface返回的是Ixxx.Stub對象,則直接調用服務端方法,沒有必要陷入內核態來執行了;

這也便是我們自定義服務實現進程間通信的簡單過程了;

在使用AIDL實現Binder通信的過程中,我們應該注意一點的就是,AIDL中不管是服務端方法還是客戶端方法都是運行在各自的Binder線程池中的,如果我們想要更新UI的話,需要用到Handler進行切換操作;

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved