android的通訊機制
㈠ [Android源碼分析] - 非同步通信Handler機制
一、問題:在Android啟動後會在新進程里創建一個主線程,也叫UI線程( 非線程安全 )這個線程主要負責監聽屏幕點擊事件與界面繪制。當Application需要進行耗時操作如網路請求等,如直接在主線程進行容易發生ANR錯誤。所以會創建子線程來執行耗時任務,當子線程執行完畢需要通知UI線程並修改界面時,不可以直接在子線程修改UI,怎麼辦?
解決方法:Message Queue機制可以實現子線程與UI線程的通信。
該機制包括Handler、Message Queue、Looper。Handler可以把消息/ Runnable對象 發給Looper,由它把消息放入所屬線程的消息隊列中,然後Looper又會自動把消息隊列里的消息/Runnable對象 廣播 到所屬線程里的Handler,由Handler處理接收到的消息或Runnable對象。
1、Handler
每次創建Handler對象時,它會自動綁定到創建它的線程上。如果是主線程則默認包含一個Message Queue,否則需要自己創建一個消息隊列來存儲。
Handler是多個線程通信的信使。比如在線程A中創建AHandler,給它綁定一個ALooper,同時創建屬於A的消息隊列AMessageQueue。然後在線程B中使用AHandler發送消息給ALooper,ALooper會把消息存入到AMessageQueue,然後再把AMessageQueue廣播給A線程里的AHandler,它接收到消息會進行處理。從而實現通信。
2、Message Queue
在主線程里默認包含了一個消息隊列不需要手動創建。在子線程里,使用Looper.prepare()方法後,會先檢查子線程是否已有一個looper對象,如果有則無法創建,因為每個線程只能擁有一個消息隊列。沒有的話就為子線程創建一個消息隊列。
Handler類包含Looper指針和MessageQueue指針,而Looper里包含實際MessageQueue與當前線程指針。
下面分別就UI線程和worker線程講解handler創建過程:
首先,創建handler時,會自動檢查當前線程是否包含looper對象,如果包含,則將handler內的消息隊列指向looper內部的消息隊列,否則,拋出異常請求執行looper.prepare()方法。
- 在 UI線程 中,系統自動創建了Looper 對象,所以,直接new一個handler即可使用該機制;
- 在 worker線程 中,如果直接創建handler會拋出運行時異常-即通過查『線程-value』映射表發現當前線程無looper對象。所以需要先調用Looper.prepare()方法。在prepare方法里,利用ThreadLocal<Looper>對象為當前線程創建一個Looper(利用了一個Values類,即一個Map映射表,專為thread存儲value,此處為當前thread存儲一個looper對象)。然後繼續創建handler, 讓handler內部的消息隊列指向該looper的消息隊列(這個很重要,讓handler指向looper里的消息隊列,即二者共享同一個消息隊列,然後handler向這個消息隊列發送消息,looper從這個消息隊列獲取消息) 。然後looper循環消息隊列即可。當獲取到message消息,會找出message對象里的target,即原始發送handler,從而回調handler的handleMessage() 方法進行處理。
- handler與looper共享消息隊列 ,所以handler發送消息只要入列,looper直接取消息即可。
- 線程與looper映射表 :一個線程最多可以映射一個looper對象。通過查表可知當前線程是否包含looper,如果已經包含則不再創建新looper。
5、基於這樣的機制是怎樣實現線程隔離的,即在線程中通信呢。
核心在於 每一個線程擁有自己的handler、message queue、looper體系 。而 每個線程的Handler是公開 的。B線程可以調用A線程的handler發送消息到A的共享消息隊列去,然後A的looper會自動從共享消息隊列取出消息進行處理。反之一樣。
二、上面是基於子線程中利用主線程提供的Handler發送消息出去,然後主線程的Looper從消息隊列中獲取並處理。那麼還有另外兩種情況:
1、主線程發送消息到子線程中;
採用的方法和前面類似。要在子線程中實例化AHandler並設定處理消息的方法,同時由於子線程沒有消息隊列和Looper的輪詢,所以要加上Looper.prepare(),Looper.loop()分別創建消息隊列和開啟輪詢。然後在主線程中使用該AHandler去發送消息即可。
2、子線程A與子線程B之間的通信。
1、 Handler為什麼能夠實現不同線程的通信?核心點在哪?
不同線程之間,每個線程擁有自己的Handler、消息隊列和Looper。Handler是公共的,線程可以通過使用目標線程的Handler對象來發送消息,這個消息會自動發送到所屬線程的消息隊列中去,線程自帶的Looper對象會不斷循環從裡面取出消息並把消息發送給Handler,回調自身Handler的handlerMessage方法,從而實現了消息的線程間傳遞。
2、 Handler的核心是一種事件激活式(類似傳遞一個中斷)的還是主要是用於傳遞大量數據的?重點在Message的內容,偏向於數據傳輸還是事件傳輸。
目前的理解,它所依賴的是消息隊列,發送的自然是消息,即類似事件中斷。
0、 Android消息處理機制(Handler、Looper、MessageQueue與Message)
1、 Handler、Looper源碼閱讀
2、 Android非同步消息處理機制完全解析,帶你從源碼的角度徹底理解
謝謝!
wingjay

㈡ Android IPC機制
IPC是指兩個進程之間進行數據交互的過程,即:跨進程通信。
進程是一個執行單,在移動設備上指一個程序或者一個應用。一個進程可以有多個線程,也可以只有一個線程,即主線程。在Android里邊,主線程也叫作UI線程,要是在主線程執行大量耗時任務,就會造成界面無法響應,ANR問題,解決這類問題,把耗時操作放在子線程就好。
在Android中,最有特色的進程間通信就是Binder,Binder輕松的實現了進程間的通信。
給四大組件 Activity、Service、Receiver、ContentProvider 在AndroidMenifeist中指定 android:process 屬性,可以指定其運行的進程。
: 開頭的線程是當前應用的私有進程,其它應用不可以和它跑在同一個進程中,而不以 : 開頭的屬於全局進程,其他應用通過ShareUID方式可以和它跑在一個進程中。
Android為了每一個應用(進程)都分配了獨立的虛擬機,不同的虛擬機在內存分配上有不同的地址空間。
多進程會造成如下幾個反面方面的問題:
為了解決這些問題,系統提供了跨進程通信方法,雖然不能直接共享內存,但是可以實現數據共享。Intent來傳遞數據,共享文件,基於Binder的Messenger,ContentProvider,AIDL和Socket。
當我們需要通過Intent和Binder傳輸數據,或者我們需要把對象持久化到存儲設備上,再或者通過網路傳輸給其它客戶端時,Serializable和Parcelable介面可以完成對象的序列化過程。
Serialzable是java提供的序列化介面,是一個空介面,為對象同序列化和反序列化操作。
想讓一個類對象實現序列化,只需要這個類實現Serialzable介面,並聲明一個serialVersionUID即可,serialVersionUID可以聲明成1L或者IDE根據當前類介面自動生成它的hash值。
沒有serialVersionUID不影響序列化,但是可能會影響反序列化。序列化時,系統當前類的serialVersionUID寫入序列化文件中,當反序列化時,回去檢測文件中的serialVersionUID,看它是否和當前類的serialVersionUID一致,如果不一致,無法完成反序列化。
Seriallizable用起來簡單但是開銷大,序列化和反序列過程需要大量的I/O操作,而Parcelable是Android序列化方式,更適合Android平台,效率更高。Parcelable主要用於內存序列化上,而Seriallizable更適用於序列化到本地存儲設備,或者將對象序列化後通過網路傳輸到別的客戶端。
Activity、Service、Receiver都支持在 Intent中傳遞Bundle數據,Bundle實現了Pareclable介面,所以它可以方便地在不同進程間傳輸。
Android基於linux,使得其並發讀寫文件可以沒有限制的進行,兩個進程可以通過讀寫一個文件來交換數據。共享數據對文件格式沒有要求,雙反約定就行。使用文件共享很有可能出問題。
SharedPreferences是個特例,雖然也是屬於文件的一種,但是由於系統對它的讀寫有一定的緩存策略,即在內存中會有一份SharedPreferences文件的緩存,因此在多進程模式下,系統對他的讀寫變得不可靠,高並發的時候,很大可能會丟失數據。
Messenger可以在不同的進程中傳遞Message對象,在Message中存入我們需要傳遞的數據,就可以實現數據的跨進程傳遞。它是一種輕量級的IPC方案,底層實現是AIDL。
Messenger對AIDL做了封裝,使得我們可以更便捷的實現跨進程通信,它一次只處理一個請求,在服務端不用考慮線程同步問題,在服務端不存在並發執行的情形。實現一個Messenger有如下幾個步驟:
在服務端創建一個Service,同時創建一個Handler,並通過它來創建一個Messenger對象,然後再Service的onBind中返回這個Messenger對象底層Binder即可。
綁定服務端Service,綁定成功後用服務端返回的IBinder對象創建一個Messenger。通過這個對象就可以向服務端發消息了。如果需要服務端回應客戶端,就需要和服務端一樣,創建一個Handler,並通過它來創建一個Messenger對象,然後把這個Messenger對象通過Message的replyTo參數傳給服務端,服務端可以通過這個replyTo參數回應客戶端。
首先要創建一個Service用來監聽客戶端的連接請求,然後創建一個AIDL文件,將暴露給客戶端的介面在這個AIDL文件中聲明,最後在Service中實現AIDL介面即可。
綁定服務端的Service,將服務端返回的Binder對象轉成AIDL介面所屬的類型,接著就可可以範文AIDL里邊的方法了。
在AIDL文件中,並不是所有的額數據類型都是可以使用的。
以上6種數據就是AIDL所支持的所有類型,其中自定義的Parecelable對象和AIDL對象必須顯示的import,不管是否和當前的AIDL文件位於同一個包。
AIDL文件中用到了自定義的Parcelable對象,必須新建一個同名的AIDL文件,在其中聲明它為parcelable類型。
AIDL中除了基礎數據類型,其它類型參數都需要標上方向:in、out、inout,in是輸入型參數,out是輸出型參數,inout是輸入輸出型參數。
上面是遠程服務端示例,AIDL方法在服務端的Binder線程池中執行,因此各個客戶端同時連接的時候,會存在多個線程同時訪問的情形,所以要在AIDL中處理線程同步,這個CopyOnWriteArrayList支持並發的讀寫。
AIDL所支持的是一個抽象的List,只是一個介面,因此雖然服務端返回的是CopyOnWriteArrayList,當時Binder會按照List規范去範文數據並最終形成一個ArrayList傳遞給客戶端。
ServiceConnection 的回調方法在UI線程中運行,服務端的方法有可能很久才能執行完畢,需要考慮ANR的問題。
服務的方法本省就運行再Binder線程池中,本身可以執行大量耗時操作,不要去服務端方法中開縣城去進行非同步任務。
客戶端
服務端
RemoteCallbackList是系統提供專門用於刪除跨進程listener的,它的內部有一個Map結構,用來保存所有的AIDL回調,這個Map的key就是Binder類型,value是CallBack類型。
客戶端解注冊的時候,我們只需要遍歷服務端所有的listener,找出那個和接注冊listener具有相同的Binder對象的服務端listener並把它刪除即可。
RemoteCallbackList的beginBroadcast和finishBroadcast必須配對使用。
ContentProvider是Android專門提供不同應用間進行數據共享的方式。底層實現一樣是Binder。
系統預置了許多ContentProvider,比如通訊錄,日程信息表,只需要通過ContentResolver的query、update、insert、delete方法即可。
㈢ Binder機制概述
Android進程間通訊是通過Binder機制來實現的,Android是基於linux系統因此有必要了解Linux系統進程相關知識.
Linux系統中(其他系統也是這樣)不同進程之間不允許直接操作或訪問另一進程.也就是進程隔離.
為了保證用戶進程不能直接訪問內核,操作系統從邏輯上將虛擬空間劃分為用戶空間和內核空間.內核程序運行在內核空間(kernel space),應用程序運行在用戶空間(user space).為了安全,他們之間是隔離的,即使用戶程序奔潰了,也不會影響內核.內核空間數據是可以共享的,用戶空間不可以.
用戶空間訪問內核空間只能通過系統調用,系統調用是用戶空間訪問內核空間的唯一方式,保證所有資源訪問在內核控制下,避免了用戶對系統資源的越權訪問,提高了系統安全性和穩定性.
_from_user:將用戶空間數據拷貝到內核空間
_to_user:將內核空間數據拷貝到用戶空間
由於用戶進程不能直接訪問硬體地址,所以系統提供了一種機制:內存映射(Memory Map).在Linux中通過調用函數mmap實現內存映射,將用戶空間一塊內存地址映射到內核空間.映射關系建立後,對用戶空間內存的修改可以反應到內核空間.內存映射可減少拷貝次數.
如果沒有內存映射,用戶進程需要訪問硬碟文件時,需要在內核空間創建一片頁緩存,將硬碟文件數據拷貝到頁緩存然後用戶進程在拷貝頁緩存數據,需要兩次拷貝.通過內存映射後硬碟文件可以和內核的虛擬內存直接映射,減少一次拷貝.
如圖
inter-process-communication進程間通信,指進程間交換數據的過程.
Linux提供了很多進程間通訊的機制,主要有管道(pipe)、消息隊列(Message)、信號(sinal)、信號量(semophore)、套接字(socket)等
內核程序在內核空間分配並開辟一塊內核緩沖區,發送進程將數據通過_from_user拷貝到內核空間的數據緩沖區,內核空間通過_to_user將數據拷貝到接收進程,這樣就實現了一次進程間通信.如圖
Linux的IPC機制有兩個問題:
1.數據通過用戶空間->內核空間->用戶空間,經過兩次拷貝,效率不高
2.接收進程無法預先知道數據大小,只能盡可能大創建數據緩沖區,或通過api消息頭獲取消息體的大小,浪費了空間或時間.
Android進程間通信通過Binder實現,下面介紹Binder機制通信原理,為什麼是Binder
內核程序創建一個數據接收緩存區,同時創建一個內核緩沖區.並建立內核緩沖區和數據接收緩沖區內存映射,以及數據內核緩沖區和接收進程用戶空間的映射關系.發送進程通過_from_user將數據拷貝到內核數據接收緩沖區,因為內核數據接收緩沖區和內核緩沖區以及接收進程用戶空間存在映射關系,相當於將數據發送到了接收進程.完成了一次進程間通信.如圖
Binder機制是c/s架構的,由Client、server、ServiceManager和Binder組成.client、server和serviceManager都是獨立的進程,由於Linux進程隔離的原因,所以需要藉助Binder進行通信.
Binder通信主要有三個步驟:注冊服務、獲取服務、使用服務.如下圖
從上一條Binder實現原理示例圖中可以看到,Binder可分為Java Binder、Native Binder和Kernal Binder.應用開發需要了解Java Binder和Navive Binder.這里只介紹Binder基本原理.具體可查看文章結尾的鏈接.
感謝
https://blog.csdn.net/itachi85/article/details/102713845
https://www.jianshu.com/p/429a1ff3560c
https://blog.csdn.net/carson_ho/article/details/73560642?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522159651217319195188353096%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=159651217319195188353096&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2 all first_rank_ecpm_v3~rank_business_v1-1-73560642.ecpm_v3_rank_business_v1&utm_term=Binder&spm=1018.2118.3001.4187
㈣ IM即時通訊開發如何實現Android版智能心跳機制
IM即時通訊開發如何實現Android版智能心跳機制。
大體思路
a)延遲心跳測試法:這是測試結果准確的前提保障,我們認為長連接建立後連續三次成功的短心跳就可以很大程度的保證下一次心跳環境是正常的。
b)成功一次認定,失敗連續累積認定:成功是絕對的,連續失敗多次才可能是失敗。
c)臨界值避免:我們使用比計算出的心跳稍微小一點的值做為穩定心跳避免臨界值。
d)動態調整:即使在一次完整的智能心跳計算過程中,我們沒有找到最好的值,我們還有機會來進行校正。
方案需考慮到影響連接壽命的思素
在Android下,不管是GCM,還是微信,都是通過TCP長連接來進行消息收發的,TCP長連接存活,消息收發就及時,所以要對影響TCP連接壽命的因素進行研究。
1、NAT超時
大部分移動無線網路運營商都在鏈路一段時間沒有數據通訊時,會淘汰 NAT 表中的對應項,造成鏈路中斷(NAT超時的更多描述見附錄9.1)。NAT超時是影響TCP連接壽命的一個重要因素(尤其是國內),所以客戶端自動測算NAT超時時間,來動態調整心跳間隔,是一個重要的優化點。
2、DHCP的租期 (lease time)
目前測試發現安卓系統對DHCP的處理有Bug,DHCP租期到了不會主動續約並且會繼續使用過期IP,這個問題會造成TCP長連接偶然的斷連。(租期問題的具體描述見附錄9.2)。
3、網路狀態變化
手機網路和WIFI網路切換、網路斷開和連上等情況有網路狀態的變化,也會使長連接變為無效連接,需要監聽響應的網路狀態變化事件,重新建立Push長連接。
心跳范圍選擇
1、前後台區分處理:
為了保證微信收消息及時性的體驗,當微信處於前台活躍狀態時,使用固定心跳。微信進入後台(或者前台關屏)時,先用幾次最小心跳維持長鏈接。然後進入後台自適應心跳計算。這樣做的目的是盡量選擇用戶不活躍的時間段,來減少心跳計算可能產生的消息不及時收取影響。
2、後台自適應心跳選擇區間:
可根據自身產品的特點選擇合適的心跳范圍。
自適應心跳演算法量化描述
因為每個網路的NAT時間可能不一致。所以需要區分計算,數據網路按subType做關鍵字,WIFI按WIFI名做關鍵字。對穩定的網路,因為NAT老化時間的存在,在自適應計算態的時候,暫設計以下步驟在當前心跳區間逼近出最大可用的心跳。 即時通訊聊天軟體app開發可以加蔚可雲的v:weikeyun24咨詢
a)變數說明:
[MinHeart,MaxHeart]——心跳可選區間。
successHeart——當前成功心跳,初始為MinHeart
curHeart——當前心跳初始值為successHeart
heartStep——心跳增加步長
successStep——穩定期後的探測步長
經過該流程,會找到必然使心跳失敗的curHeart(或者MaxHeart),為了保險起見,我們選擇比前一個成功值稍微小一點的值作為後台穩定期的心跳間隔。
影響手機網路測試的因素太多,為了盡量保證測試結果的可靠性,我們使用延遲心跳測試法。在我們重新建立TCP連接後,先使用 短心跳連續成功三次,我們才認為網路相對穩定,可以使用curHeart進行一次心跳測試。圖4-2顯示了一次有效心跳測試過程。圖4-3顯示了在沒有達到穩定網路環境時,我們會一直使用固定短心跳直到滿足三次連續短心跳成功。
使用延遲心跳測試的好處是,可以剔除偶然失敗,和網路變化較大的情況(如地鐵),使測試結果相對可靠(五次延遲測試確定結論)。同時在網路波動較大的情況,使用短心跳,保證收取消息相對及時。
c)運行時的動態調整策略(已經按測算心跳穩定值後)
NAT超時值算出來後,在維持心跳的過程中的策略。
- 無網路、網路時好時壞、偶然失敗、NAT超時變小:
在後台穩定期發生心跳發生失敗後,我們使用延遲心跳測試法測試五次。如果有一次成功,則保持當前心跳值不變;如果五次測試全失敗,重新計算合理心跳值。該過程如圖4-4所示,有一點需要注意,每個新建的長連接需要先用短心跳成功維持3次後才用successHeart進行心跳。
NAT超時變大:
以周為周期,每周三將後台穩定態調至自適應計算態,使用心跳延遲法往後探測心跳間隔。
- successHeart是NAT超時臨界值:
因為我們現在選擇的是一個比successHeart稍小的值作為穩定值,所以在計算過程中可以避開臨界值。當運營商在我們後台穩定期將NAT超時調整為我們當前計算值,那麼由於我們每周會去向下探索,所以下一周探測時也可以及時調整正確。
d)冗餘Sync和心跳
在用戶的一些主動操作以及聯網狀態改變時,增加冗餘Sync和心跳,確保及時收到消息。
1、當用戶點亮屏幕的時候,做一次心跳。
2、當微信切換到前台時,做一次Sync。
3、聯網時重建信令TCP,做一次Sync。
可能存在的風險及預防措施
DHCP租期因素:
1、問題:根據目前的測試結果顯示,安卓不續約到期的IP Bug,會導致TCP連接在不確定的時間點失效,從而會導致一次心跳失敗。
2、預防:統計後台穩定期的心跳成功率,上報給後台。後台可以按地區分網路監控這個指標的波動,並且後台可以根據不同的波動,動態調整某區域特定網路下可選的心跳區間。
NAT超時介紹
因為 IP v4 的 IP 量有限,運營商分配給手機終端的 IP 是運營商內網的 IP,手機要連接 Internet,就需要通過運營商的網關做一個網路地址轉換(Network Address Translation,NAT)。簡單的說運營商的網關需要維護一個外網 IP、埠到內網 IP、埠的對應關系,以確保內網的手機可以跟 Internet 的伺服器通訊。
㈤ Android消息機制和原理
Android消息機制及其原理
Handle的原理
andriod提供了Handler和Looper來滿足線程間的通信。Handler先進先出原則。Looper類用來管理特定線程內對象之間的消息交換(MessageExchange)。
MessageQueue
MessageQueue是持有Message(在Looper中派發)的一個鏈表,Message並不是直接添加到MessageQueue中的,而是通過與Looper相關聯的Handler來進行的。
用來存放線程放入的消息,讀取會自動刪除消息,單鏈表維護,在插入和刪除上有優勢。在其next()中會無限循環,不斷判斷是否有消息,有就返回這條消息並移除。
Looper
一個線程可以產生一個Looper對象,由它來管理此線程里的MessageQueue
Looper創建的時候會創建一個MessageQueue,調用loop()方法的時候消息循環開始,loop()也是一個死循環,會不斷調用messageQueue的next(),當有消息就處理,否則阻塞在messageQueue的next()中。當Looper的quit()被調用的時候會調用messageQueue的quit(),此時next()會返回null,然後loop()方法也跟著退出。
MessageQueue和Looper是一對一關系,Handler和Looper是多對一
Handler
在主線程構造一個Handler,與Looper溝通,以便push新消息到MessageQueue里;
接收Looper從MessageQueue取出Handler所送來的消息。然後在其他線程調用sendMessage(),此時主線程的MessageQueue中會插入一條message,然後被Looper使用.
Thread
UIthread 通常就是main thread,而Android啟動程序時會替它建立一個MessageQueue,系統的主線程在ActivityThread的main()為入口開啟主線程,其中定義了一系列消息類型,包含四大組件的啟動停止。
消息隊列分發演算法源碼
每個message之間拉手,知道自己前面和後面的message
message通過時間戳來排序,小的在前
配合handle取出message,message時間到,就去除隊列首個message,取出之後置為null,第二個message就排在第一,類推
//消息的存放
boolean enqueueMessage(Message msg, long when) {
synchronized (this) {
msg.when = when;
Message p = mMessages; //註解1
if (p == null || when == 0 || when < p.when){
msg.next = p;
mMessages = msg; //註解2
} else {
Message prev;
for (;;) { //註解3
prev = p;
p = p.next;
if (p == null || when < p.when) {
break;
}
}
msg.next = p;
prev.next = msg;
}
}
return true;
}
㈥ Android通信方式篇(七)-Binder機制(Native層(下))
本篇文章針對向ServiceManager注冊服務 和 獲取服務兩個流程來做總結。在這兩個過程中,ServiceManager都扮演的是服務端,與客戶端之間的通信也是通過Binder IPC。
在此之前先了解下Binder的進程與線程的關系:
用戶空間 :ProcessState描述一個進程,IPCThreadState對應一個進程中的一個線程。
內核空間 :binder_proc描述一個進程,統一由binder_procs全局鏈表保存,binder_thread對應進程的一個線程。
ProcessState與binder_proc是一一對應的。
Binder線程池 :每個Server進程在啟動時會創建一個binder線程池,並向其中注冊一個Binder線程;之後Server進程也可以向binder線程池注冊新的線程,或者Binder驅動在探測到沒有空閑binder線程時會主動向Server進程注冊新的的binder線程。對於一個Server進程有一個最大Binder線程數限制15,(#define DEFAULT_MAX_BINDER_THREADS 15)。對於所有Client端進程的binder請求都是交由Server端進程的binder線程來處理的。我的理解是:binder線程是進程進行binder ipc時的一條數據處理路徑。
MediaPlayerService向ServiceManager注冊過程如下:
相關類:
整個過程總結如下:
1 獲取BpServiceManager 與 BpBinder
由defaultServiceManager()返回的是BpServiceManager,同時會創建ProcessState對象和BpBinder對象。然後通過BpBinder執行transact,把真正工作交給IPCThreadState來處理。
2 BpBinder transact
Binder代理類調用transact()方法,真正工作還是交給IPCThreadState來進行transact工作。
3 通過IPCThreadState 包裝並轉換數據並進行transact事務處理
每個線程都有一個IPCThreadState,每個IPCThreadState中都有一對Parcel變數:mIn、mOut。相當於兩根數據管道:
最後執行talkWithDriver。
writeTransactionData:將BC Protocol + binder_transaction_data結構體 寫入mOut, 然後執行waitForResponse:
由talkWithDriver將數據進一步封裝到binder_write_read結構體,通過ioctl(BINDER_WRITE_READ)與驅動通信。同時等待驅動返回的接收BR命令,從mIn取出返回的數據。
mIn包裝的數據結構(注冊服務handle = 0 ,code 為ADD_SERVICE_TRANSACTION):
4 Binder Driver
把binder_write_read結構體write_buffer里數據取出來,分別得到BC命令和封裝好數據的事務binder_transaction_data, 然後根據handler,在當前binder_proc中,找到相應的binder_ref,由binder_ref再找到目標binder_node實體,由目標binder_node再找到目標進程binder_proc。然後就是插入數據:當binder驅動可以找到合適的線程,就會把binder_transaction節點插入到servciemanager的線程的todo隊列中,如果找不到合適的線程,就把節點之間插入servciemanager的binder_proc的todo隊列。
5 ServiceManager
經過Binder Driver的處理,數據已經到了ServiceManager進程,在BR_TRANSACTION的引導下,在binder_loop()中執行binder_parser()取出數據,執行do_add_service()操作,最終向 svcinfo 列表中添加已經注冊的服務(沒有數據的返回)。最後發送 BR_REPLY 命令喚醒等待的線程,通知注冊成功。結束MediaPlayerService進程 waitForResponse()的狀態,整個注冊過程結束。
獲取服務的過程與注冊類似,首先 ServiceManager 向 Binder 驅動發送 BC_TRANSACTION 命令攜帶 CHECK_SERVICE_TRANSACTION 命令,同時獲取服務的線程進入等待狀態 waitForResponse()。Binder 驅動收到請求命令向 ServiceManager 的發送 BC_TRANSACTION 查詢已注冊的服務,會區分請求服務所屬進程情況。
查詢到直接響應 BR_REPLY 喚醒等待的線程。若查詢不到將與 binder_procs 鏈表中的服務進行一次通訊再響應。
以startService為例來簡單總結下執行流程:
3.1 從方法執行流程來看:
Client :
1 AMP.startService 標記方法以及通過Parcel包裝數據;
2 BinderProxy.transact 實際調用native的 android_os_BinderProxy_transact 傳遞數據;
3 獲取BpServiceManager 與 BpBinder 同時會創建ProcessState。然後通過BpBinder執行transact,把真正工作交給IPCThreadState來處理;
4 IPC.transact 主要執行writeTransactionData,將上層傳來的數據重新包裝成binder_transaction_data,並將BC Protocol + binder_transaction_data結構體 寫入mOut;
5 IPC waitForResponse talkWithDriver + 等待返回數據;
6 talkWithDriver 將數據進一步封裝成binder_write_read,通過ioctl(BINDER_WRITE_READ)與驅動通信;
Kernel :
7 binder ioctl 接收BINDER_WRITE_READ ioctl命令;
8 binder_ioctl_write_read 把用戶空間數據ubuf拷貝到內核空間bwr;
9 binder_thread_write 當bwr寫緩存有數據,則執行binder_thread_write;當寫失敗則將bwr數據寫回用戶空間並退出;
10 binder_transaction 找到目標進程binder_proc並插入數據到目標進程的線程todo隊列,最終執行到它
時,將發起端數據拷貝到接收端進程的buffer結構體;
11 binder_thread_read 根據binder_transaction結構體和binder_buffer結構體數據生成新的binder_transaction_data結構體,寫入bwr的read_buffer,當bwr讀緩存有數據,則執行binder_thread_read;當讀失敗則再將bwr數據寫回用戶空間並退出;最後,把內核數據bwr拷貝到用戶空間ubuf。
12 binder_thread_write + binder_ioctl BR命令和數據傳遞
Server:
13 IPC.executeCommand 解析kernel傳過來的binder_transaction_data數據,找到目標BBinder並調用其transact()方法;
14 IPC.joinThreadPool 採用循環不斷地執行getAndExecuteCommand()方法, 處理事務。當bwr的讀寫buffer都沒有數據時,則阻塞在binder_thread_read的wait_event過程. 另外,正常情況下binder線程一旦創建則不會退出.
15 BBinder.transact 到Binder.exeTransact 調用 AMN.onTransact
16 AMN.onTransact 把數據傳遞到AMS.starService去執行
17 AMS.starService Server處理了Client的請求了
然後原路replay回去,talkWithDriver 到Kernel ,然後找到Client進程,把數據拷貝到read_buffer里,最終喚醒IPC,把反饋傳遞回AMP.startService。完成啟動服務。
3.2 從通信協議流程來看:
非oneWay:
oneway:
oneway與非oneway區別: 都是需要等待Binder Driver的回應消息BR_TRANSACTION_COMPLETE. 主要區別在於oneway的通信收到BR_TRANSACTION_COMPLETE則返回,而不會再等待BR_REPLY消息的到來. 另外,oneway的binder IPC則接收端無法獲取對方的pid.
3.3 從數據流來看
從用戶空間開始:
進入驅動後:
回到用戶空間:
參考:
http://gityuan.com/2016/09/04/binder-start-service/
http://gityuan.com/2015/11/28/binder-summary/
http://gityuan.com/2015/11/14/binder-add-service/
http://gityuan.com/2015/11/15/binder-get-service/
㈦ 在android中進程間通信機制是怎樣的
一般都是基於ARM處理器的吧 安卓的內核也是基於Linux的吧。 網路實現依靠TCP/IP協議棧實現實行封包和解包以及連接的建立和控制,還涉及到你手機的硬體網卡等。 進程間通信方式一般採用的消息隊列,共享內存,套接字,還有管道了。 多線程是由操作系統來管理每個線程的CPU時間和資源的分配。也是比較復雜的,涉及到線程間通信,線程同步等。 內存管理是由操作系統進行分段,分頁。分配機制比較復雜的,涉及到碎片的減少,內存的回收等。 要想了解詳細內容,可以看看Linux操作系統原理。或者google提供的相關文檔。
㈧ android中的進程通信和Binder機制
如果統觀Binder中的各個組成元素,就會驚奇的發現它和TCP/IP網路有很多相似之處:
首先Binder是android進程間通信的一種方式,
基本原理:binder定義了4個角色:client,server,serviceManager ,binder驅動
server會創建一個binder實體並起一個名字,然後將名字一塊以數據包的形式通過binder驅動發送給serviceManager ,通知servicemanager注冊一個名字為xx的Binder,然後client通過名字查詢到該Binder 的引用。
注意
㈨ Android面試必問handler機制淺析
Handler是Android中的非同步消息處理機制。當發送一個消息之後,這個消息是進入一個消息隊列(MessageQueue),在消息隊列中通過Looper去循環的獲取隊列中的消息,然後將消息分派給對應的處理者進行處理。
Message:存儲需要處理操作的信息
MessageQueue:先進先出,存儲handler發送過來的消息
Looper:循環器,它是消息隊列和handler的通信媒介,1:循環的取出消息隊列中的消息;2:將取出的消息發送給對應的處理者
Handler:主線程和子線程的通信媒介,1:添加消息到消息隊列; 2:處理循環器分派過來的消息
在handler機制中,Looper.loop方法會不斷循環獲取Message, 其中的消息的獲取是通過調用MessageQueue的next()方法獲取的,而該方法會調用nativePollOnce()方法 ,這是一個native方法。底層的實現涉及到Linux pipe/epoll機制,nativePollOnce()被阻塞時,主線程會釋放CPU資源,進入休眠狀態. 直到下個消息到達或者有事務發生,會通過pipe管道寫端寫入數據來喚醒looper工作。
Android6.0及以前的版本使用管道與epoll來完成Looper的休眠與喚醒的
Android6.0及以後的版本使用eventfd與epoll來完成Looper的休眠與喚醒的
如果不處理的話,會阻塞線程,處理方案是調用Looper的quit()(清空所有的延遲和非延遲的消息)和quitSafely()(只清空延遲消息); 這個方法會調用MessageQueue的quit()方法,清空所有的Message,並調用nativeWake()方法喚醒之前被阻塞的nativePollOnce(),使得方法next()方法中的for循環繼續執行,接下來發現Message為null後就會結束循環,Looper結束。如此便可以釋放內存和線程
同進程線程間內存共享,通過handler通信,消息的內容是不需要從一個線程拷貝到另一個線程,因為兩個線程間可使用的內存是同一個區域。(注意:線程私有區域ThreadLocal)
管道的作用就是當一個線程准備好Message,並放入消息池,這時需要通知了一個線程B去處理這個消息。線程A向管道的寫端寫入數據,管道有數據便會喚醒線程B去處理消息。管道的作用是用於通知另一個線程的,這便是最核心的作用。
從內存角度,通信過程中binder涉及到一次內存拷貝,handler機制中的Message根本不需要拷貝,本身就是在同一片內存。
從CPU角度,為了Binder通信底層驅動還需要創建一個binder線程池,每次通信涉及binder線程的創建和內存的分配等比較浪費CPU資源
原因:handler發送的消息在當前handler的消息隊列中,如果此時activity被finish掉了,那麼消息隊列的消息依舊由handler進行處理,若此時handler申明為內存類(非靜態內部類),內部類持有外部類的實例引用,這樣在GC垃圾回收時發現Activity還有其他引用存在,因而就不會去回首這個Activity,進而導致Activity泄漏。
方法:使用靜態內部類,並且使用WeakReference包裹外部類的對象。首先靜態內部類不持有外部類的引用,使用靜態的handler不會導致activity的泄漏,handler定義static的同時,還要用WeakReference包裹外部類的對象。
㈩ Android跨進程通信
本文整理和引用他人的筆記,旨在個人復習使用。
參考鏈接:
https://blog.csdn.net/fanleiym/article/details/83894399
https://github.com/274942954/AndroidCollection/blob/master/Docs/Android%E7%9F%A5%E8%AF%86%E7%82%B9%E6%B1%87%E6%80%BB.md#%E8%BF%9B%E7%A8%8B%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F
https://www.kaelli.com/4.html
https://carsonho.blog.csdn.net/article/details/73560642?utm_medium=distribute.pc_relevant.none-task-blog--1.e_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog--1.e_weight
默認情況下,一個app只會運行在一個進程中,進程名為app的包名。
1. 分散內存的佔用
Android系統對每個應用進程的內存佔用是有限制的,佔用內存越大的進程,被系統殺死的可能性就越大。使用多進程可以減少主進程佔用的內存,避免OOM問題,降低被系統殺死的概率。
2. 實現多模塊
一個成熟的應用一定是多模塊化的。項目解耦,模塊化,意味著開辟新的進程,有獨立的JVM,帶來數據解耦。模塊之間互不幹預,團隊並行開發,同時責任分工也很明確。
3. 降低程序奔潰率
子進程崩潰不會影響主進程的運行,能降低程序的崩潰率。
4. 實現一些特殊功能
比如可以實現推送進程,使得主進程退出後,能離線完成消息推送服務。還可以實現守護進程,來喚醒主進程達到保活目的。還可以實現監控進程專門負責上報bug,進而提升用戶體驗。
android:process 屬性的值以冒號開頭的就是 私有進程 ,否則就是 公有進程 。當然命名還需要符合規范,不能以數字開頭等等。
1. 前台進程
2. 可見進程
3. 服務進程
4. 後台進程
5. 空進程
Android 會將進程評定為它可能達到的最高級別。另外服務於另一進程的進程其級別永遠不會低於其所服務的進程。
創建新的進程時會創建新的Application對象,而我們通常在Application的onCreate方法中只是完成一些全局的初始化操作,不需要多次執行。
解決思路:獲取當前進程名,判斷是否為主進程,只有主進程的時候才執行初始化操作
獲取當前進程名的兩種方法:
Application中判斷是否是主進程(方法1例子):
Serializable 和 Parcelable是數據序列化的兩種方式,Android中只有進行序列化過後的對象才能通過intent和Binder傳遞。
通常序列化後的對象完成傳輸後,通過反序列化獲得的是一個新對象,而不是原來的對象。
Serializable是java介面,位於java.io的路徑下。Serializable的原理就是把Java對象序列化為二進制文件後進行傳遞。Serializable使用起來非常簡單,只需直接實現該介面就可以了。
Parcelable是Google為了解決Serializable效率低下的問題,為Android特意設計的一個介面。Parcelable的原理是將一個對象完全分解,分解成可以傳輸的數據類型(如基本數據類型)再進行傳遞。
通常需要存到本地磁碟的數據就使用Serializable,其他情況就使用效率更高的Parcelable。
IPC 即 Inter-Process Communication (進程間通信)。Android 基於 Linux,而 Linux 出於安全考慮,不同進程間不能之間操作對方的數據,這叫做「進程隔離」。
每個進程的虛擬內存空間(進程空間)又被分為了 用戶空間和內核空間 , 進程只能訪問自身用戶空間,只有操作系統能訪問內核空間。
由於進程只能訪問自身用戶空間,因此在傳統的IPC中,發送進程需要通過_from_user(系統調用)將數據從自身用戶空間拷貝到內核空間,再由接受進程通過_to_user從內核空間復拷貝到自身用戶空間,共需要拷貝2次,效率十分低下。Android採用的是Binder作為IPC的機制,只需復制一次。
Binder翻譯過來是粘合劑,是進程之間的粘合劑。
Binder IPC通信的底層原理是 通過內存映射(mmap),將接收進程的用戶空間映射到內核空間 ,有了這個映射關系,接收進程就能通過用戶空間的地址獲得內核空間的數據,這樣只需發送進程將數據拷貝到內核空間就可完成通訊。
一次完整的Binder IPC通信:
從IPC的角度看,Binder是一種跨進程通信機制(一種模型),Binder 是基於 C/S 架構的,這個通信機制中主要涉及四個角色:Client、Server、ServiceManager和Binder驅動。
Client、Server、ServiceManager都是運行在用戶空間的進程,他們通過系統調用(open、mmap 和 ioctl)來訪問設備文件/dev/binder,從而實現與Binder驅動的交互。Binder驅動提供進程間通信的能力(負責完成一些底層操作,比如開辟數據接受緩存區等),是Client、Server和ServiceManager之間的橋梁。
Client、Server就是需要進行通信兩個的進程,通信流程:
細心的你一定發現了,注冊服務和獲得服務本身就是和ServiceManager進行跨進程通信。其實和ServiceManager的通信的過程也是獲取Binder對象(早已創建在Binder驅動中,攜帶了注冊和查詢服務等介面方法)來使用,所有需要和ServiceManager通信的進程,只需通過0號引用,就可以獲得這個Binder對象了。
AIDL內部原理就是基於Binder的,可以藉此來分析Binder的使用。
AIDL是介面定義語言,簡短的幾句話就能定義好一個復雜的、內部有一定功能的java介面。
先看看ICallBack.aidl文件,這里定義了一個介面,表示了服務端提供的功能。
被定義出來的java介面繼承了IInterface介面,並且內部提供了一個Stub抽象類給服務端(相當於封裝了一下,服務端只需繼承這個類,然後完成功能的裡面具體的實現)。
參考: https://www.cnblogs.com/sqchen/p/10660939.html
(以下是添加了回調的最終實現,可以看參考鏈接一步一步來)
為需要使用的類,創建aidl文件。
系統會自動在main文件下生成aidl文件夾,並在該文件夾下創建相應目錄。
在java相同路徑下創建Student類,這里不能使用@Parcelize註解,否則會報錯
創建IStudentService.aidl,定義了一個介面,該介面定義了服務端提供的功能。創建完後rebuild一下項目 (每次創建和修改定義介面文件都要rebuild一下)
創建在服務端的StudentService
可以看見有回調,說明客戶端也提供了介面給服務端來回調(雙向通信,此時客戶端的變成了服務端),即ICallBack.aidl
客戶端是通過Binder驅動返回的Binder調用StudentService里的具體實現方法
AIDL使用注意:
Messenger可以在不同進程中傳遞 Message 對象,在Message中放入我們需要傳遞的數據,就可以輕松地實現數據的進程間傳遞了。Messenger 是一種輕量級的 IPC 方案,是對AIDL的封裝,底層實現是 AIDL。
使用詳見: https://blog.csdn.net/qq951127336/article/details/90678698