在Android開發過程當中,Binder的身影無處無處不在,咱們編寫的程序都使用過Binder機制(例如startActivity的執行過程)可是請問你知道什麼是Binder麼?在開發過程當中你察覺到它的存在了麼?linux
Binder翻譯過來是「膠水「的意思,這個翻譯分形象。Binder的主要工做就是淡化哦了進程邊界,淡化了進程間通訊的過程。要是想更好的理解Binder就必須從Linux進程談起。android
爲了保護進程空間不被別的進程破壞活着干擾,Linux中的進程是相互獨立的,也就是所謂的進程隔離。(並且一個進程的內存空間還被分爲了用戶空間和內核空間,兩者也是相互隔離的。這裏不作探討)因此在Linux中,進程與進程之間是相互隔離的,並且進程中的用戶和內核空間也是隔離的。安全
也就是說爲了安全和獨立,一個進程是不能直接操做或者訪問另一個進程的內存空間的。他們之間既然是隔離的,在須要通訊協做的時候就須要使用進程間通訊技術(即IPC,也稱跨進程通訊),咱們都知道Android框架是創建在Linux之上的,當讓也會面對進程間通信的問題。架構
在Linux系統中爲了達到進程間通信的目的,咱們能夠選用諸如管道、Socket等技術手段,那麼爲何Android選用了Binder這種新型的IPC技術,放棄了原有成熟的技術呢?框架
主要有以下兩個方面:
1. 性能角度函數
因爲在移動設備諸如省電等性能的考慮,普遍地使用進程間通信對於通訊機制的性能有嚴格的要求,Binder相對於傳統的Socket、管道方式更加高效。Bidner數據拷貝只須要一次,而管道、消息隊列、Socket都須要2次,內存共享方式一次內存拷貝都不須要,可是實現起來難度高,複雜性大。佈局
2.穩定性角度性能
傳統的進程通訊方式對於通訊雙方的身份並無作出嚴格的驗證,好比Socket通訊ip地址是客戶端手動填入,很容易進行僞造,而Binder機制從協議自己就支持對通訊雙方作身份校檢,於是大大提高了安全性。spa
每一個Android的進程,只能運行在本身進程所擁有的虛擬地址空間。對應一個4GB的虛擬地址空間,其中3GB是用戶空間,1GB是內核空間,固然內核空間的大小是能夠經過參數配置調整的。對於用戶空間,不一樣進程之間彼此是不能共享的,而內核空間倒是可共享的。Client進程向Server進程通訊,偏偏是利用進程間可共享的內核內存空間來完成底層通訊工做的,Client端與Server端進程每每採用ioctl等方法跟內核空間的驅動進行交互。.net
首先咱們看看咱們的程序跨進程調用系統服務的簡單示例,實現浮動窗口部分代碼:
//獲取WindowManager服務引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//佈局參數layoutParams相關設置略... View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null); //添加view wm.addView(view, layoutParams);
系統服務都是運行在systemServer進程中,所以咱們調用系統服務都是跨進程的調用。第2行代碼中,獲得的wm是WindowManager對象的引用,第6行調用WindowManager的addView函數,將觸發遠程調用,調用的是運行在systemServer進程中的WindowManager的addView函數。那麼addView函數作了什麼呢?咱們繼續看。
假設你已經建立好服務端類MyService、客戶端類MyClient。在客戶端持有MyService的引用,而且調用了MyService的func函數,那麼Android內部調用過程以下:
看了這個圖之後,相信你對你的代碼在調用遠程進程函數時有個全局的認識。這張圖有一點很重要,就是客戶端當前線程會被掛起!所以,若是遠程進程是執行長時間的運算,請不要使用主線程去調用遠程函數,以防止ANR。
上面一節咱們對遠程進程調用代碼執行過程有個初步瞭解,在Android開發中,咱們大量使用到了系統Service,好比媒體播放、各類傳感器以及WindowManagerService等等等等(太多了~)。那麼Android是怎麼管理這些服務,而且讓用戶跨進程調用這些服務呢?首先咱們看看調用系統服務的過程。在Android開機啓動過程當中,Android會初始化系統的各類Service,並將這些Service向ServiceManager註冊(即讓ServiceManager管理)。客戶端想要獲得具體的Service直接向ServiceManager要便可。客戶端首先向ServiceManager查詢獲得具體的Service引用,而後經過這個引用向具體的服務端發送請求,服務端執行完成後就返回。
服務端跨進程的類都要繼承Binder類。咱們所持有的Binder引用(即服務端的類引用)並非實際真實的遠程Binder對象,咱們的引用在Binder驅動裏還要作一次映射。也就是說,設備驅動根據咱們的引用對象找到對應的遠程進程。客戶端要調用遠程對象函數時,只需把數據寫入到Parcel,在調用所持有的Binder引用的transact()函數,transact函數執行過程當中會把參數、標識符(標記遠程對象及其函數)等數據放入到Client的共享內存,Binder驅動從Client的共享內存中讀取數據,根據這些數據找到對應的遠程進程的共享內存,把數據拷貝到遠程進程的共享內存中,並通知遠程進程執行onTransact()函數,這個函數也是屬於Binder類。遠程進程Binder對象執行完成後,將獲得的寫入本身的共享內存中,Binder驅動再將遠程進程的共享內存數據拷貝到客戶端的共享內存,並喚醒客戶端線程。
好了,如今對Binder機制已經理解了,咱們再看看Android是怎麼運用Binder的。再現前面代碼:
//獲取WindowManager服務引用
WindowManager wm = (WindowManager)getSystemService(getApplication().WINDOW_SERVICE);
//佈局參數layoutParams相關設置略... View view=LayoutInflater.from(getApplication()).inflate(R.layout.float_layout, null); //添加view wm.addView(view, layoutParams);
這段代碼前面已經出現過。getSystemService(getApplication().WINDOW_SERVICE);函數內部原理就是向ServiceManager查詢標識符爲getApplication().WINDOW_SERVICE的遠程對象的引用。即WindowManager對象的引用,這個引用的真正實現是WindowManager的某個代理。獲得這個引用後,在調用addView時,真正的實現是在代理裏面,代理把參數打包到Parcel對象中,而後調用transact函數(該函數繼承自Binder),再觸發Binder驅動的一系列調用過程。