基本上參考Android跨進(jìn)程通信:圖文詳解 Binder機(jī)制 原理再結(jié)合自己的理解記錄本次學(xué)習(xí)
Binder總結(jié)
Binder驅(qū)動(dòng)
全部流程表
接下來(lái)就來(lái)理解并扒一下其中的細(xì)節(jié)
什么是進(jìn)程間通訊,為什么要搞一個(gè)Binder機(jī)制
一個(gè)進(jìn)程空間分為 用戶空間和內(nèi)核空間(kernel)
用戶空間:數(shù)據(jù)不可共享=不可共享空間
內(nèi)核空間:數(shù)據(jù)可共享=可共享空間
所有進(jìn)程共用1各內(nèi)核空間
內(nèi)核空間咽瓷、用戶空間、內(nèi)核態(tài)寒矿、用戶態(tài)
對(duì)于32位操作系統(tǒng)而言国裳,它的尋址空間(虛擬地址空間)為4G。操作系統(tǒng)將虛擬地址空間劃分為兩部分、一部分內(nèi)核空間(最高1G字節(jié))弃甥,一部分用戶空間(3G字節(jié))
內(nèi)核態(tài):當(dāng)進(jìn)程運(yùn)行在內(nèi)核空間,CPU執(zhí)行任何指令汁讼,可以自由的訪問(wèn)任何有效地址
用戶態(tài):當(dāng)進(jìn)程運(yùn)行在用戶空間淆攻,只能范文映射其地址空間的頁(yè)表項(xiàng)中規(guī)定的用戶態(tài)下可訪問(wèn)頁(yè)面的虛擬地址
為什么要區(qū)分內(nèi)核空間與用戶空間
CPU將指令分為特權(quán)指令和非特權(quán)指令,對(duì)于危險(xiǎn)的指令嘿架,比如清內(nèi)存等瓶珊,這種指令只允許操作系統(tǒng)及其相關(guān)模塊使用,普通應(yīng)用只能使用普通指令耸彪,所以Ring3級(jí)別被運(yùn)行在用戶態(tài)伞芹,Ring0級(jí)別被成為運(yùn)行在內(nèi)核態(tài)
用戶空間與內(nèi)核空間的交互
進(jìn)程內(nèi)兩個(gè)空間的交互需要通過(guò)系統(tǒng)調(diào)用,主要函數(shù)
copy_from_user():將用戶空間的數(shù)據(jù)拷貝到內(nèi)核空間
copy_to_user():將內(nèi)核空間的數(shù)據(jù)拷貝到用戶空間
進(jìn)程隔離&跨進(jìn)程通訊(IPC)
進(jìn)程間傳遞數(shù)據(jù)必須要經(jīng)過(guò)內(nèi)核空間而 發(fā)送到內(nèi)核 和從內(nèi)核拿出給另一個(gè)進(jìn)程時(shí)需要兩次數(shù)據(jù)的拷貝
Binder的優(yōu)勢(shì)
binder機(jī)制則只需要1次數(shù)據(jù)拷貝蝉娜,它通過(guò)Binder驅(qū)動(dòng)負(fù)責(zé)管理數(shù)據(jù)接受關(guān)村唱较,原理是內(nèi)存映射mmap系統(tǒng)調(diào)用、它連接兩個(gè)進(jìn)程召川,實(shí)現(xiàn)了mmap()系統(tǒng)調(diào)用南缓,主要負(fù)責(zé)創(chuàng)建數(shù)據(jù)接收的緩沖空間&管理數(shù)據(jù)接收緩存
內(nèi)存映射 mmap()
(memory map)含義
關(guān)聯(lián)進(jìn)程中的1個(gè)虛擬內(nèi)存區(qū)域 & 1個(gè)磁盤(pán)上的對(duì)象,使二者存在映射關(guān)系
這樣如果從緩沖區(qū)中取數(shù)據(jù)扮宠,就相當(dāng)于讀文件中的相應(yīng)字節(jié)西乖,而將數(shù)據(jù)存入緩沖區(qū)狐榔,就相當(dāng)于寫(xiě)文件中的相應(yīng)字節(jié),這樣不需要read和write也能直接之心I/O了
就是操作內(nèi)存就可以操作磁盤(pán)中的對(duì)象了
示意圖
如果多個(gè)進(jìn)程虛擬內(nèi)存區(qū)域和同1個(gè)共享對(duì)象获雕,簡(jiǎn)歷映射關(guān)系后薄腻,若1個(gè)進(jìn)程對(duì)該虛擬區(qū)域進(jìn)行寫(xiě)操作,那么對(duì)于也把共享對(duì)象映射到其自身虛擬內(nèi)存區(qū)域的進(jìn)程也是可見(jiàn)的
實(shí)現(xiàn)過(guò)程
mmap()的返回值是內(nèi)存映射在用戶空間的地址
該函數(shù)的作用 = 創(chuàng)建虛擬內(nèi)存區(qū)域 + 與共享對(duì)象建立映射關(guān)系
/** * 函數(shù)原型 */
void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset);
/** * 具體使用(用戶進(jìn)程調(diào)用mmap())
? * 下述代碼即常見(jiàn)了一片大小 = MAP_SIZE的接收緩存區(qū)
? & 關(guān)聯(lián)到共享對(duì)象中(即建立映射) */
fd = open("/dev/binder", O_RDWR);
mmap(NULL, MAP_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
? /** * 內(nèi)部原理 *
? 步驟1:創(chuàng)建虛擬內(nèi)存區(qū)域 *
? 步驟2:實(shí)現(xiàn)地址映射關(guān)系届案,即:進(jìn)程的虛擬地址空間 ->> 共享對(duì)象
? * 注: * a. 此時(shí)庵楷,該虛擬地址并沒(méi)有任何數(shù)據(jù)關(guān)聯(lián)到文件中,僅僅只是建立映射關(guān)系
? * b. 當(dāng)其中1個(gè)進(jìn)程對(duì)虛擬內(nèi)存寫(xiě)入數(shù)據(jù)時(shí)楣颠,則真正實(shí)現(xiàn)了數(shù)據(jù)的可見(jiàn)
? */
具體工作流程
Binder 模型
模型原理圖
基于CS模型即Client-Server 模式
模型組成角色說(shuō)明
角色作用備注
Client 進(jìn)程使用服務(wù) 的進(jìn)程Andoird 客戶端 各種App
Server 進(jìn)程提供服務(wù) 的進(jìn)程服務(wù)端 System_server
ServiceManager進(jìn)程管理Service查找和注冊(cè)服務(wù)
(將字符形式的Binder名字 轉(zhuǎn)化成Client中對(duì)該Binder 的引用)
由init進(jìn)程啟動(dòng)的進(jìn)程尽纽,作為Binder IPC通訊的守護(hù)進(jìn)程
功能類似路由器
Binder 驅(qū)動(dòng)一種虛擬設(shè)備驅(qū)動(dòng),是連接Service進(jìn)程童漩、Client進(jìn)程和ServiceManager進(jìn)程的橋梁弄贿,它們之間的交互(使用open和ioctl文件操作函數(shù))而非直接交互
1.傳遞進(jìn)程間的數(shù)據(jù):通過(guò)內(nèi)存映射
2.實(shí)現(xiàn)線程控制:采用Binder的線程池,并由Binder驅(qū)動(dòng)自身進(jìn)行管理
Binder驅(qū)動(dòng)持有每個(gè)Service進(jìn)程在內(nèi)核空間中的Binder實(shí)體矫膨,并給Client進(jìn)程提供Binder實(shí)體的引用Proxy
模型原理步驟說(shuō)明
簡(jiǎn)單總結(jié)
注冊(cè)服務(wù):
系統(tǒng)Server服務(wù)進(jìn)程在啟動(dòng)時(shí)會(huì)先把所有服務(wù)注冊(cè)到ServiceManager進(jìn)程
獲取服務(wù):
Client進(jìn)程發(fā)起請(qǐng)求傳入獲取的服務(wù)名-》Binder驅(qū)動(dòng)將請(qǐng)求-》SeviceManager進(jìn)程找到所需服務(wù)后通過(guò)Binder驅(qū)動(dòng)返回給Client進(jìn)程差凹,此時(shí)返回的是代理對(duì)象 除非Client與Server在同一進(jìn)程
使用服務(wù):
Binder驅(qū)動(dòng):為跨進(jìn)程做準(zhǔn)備實(shí)現(xiàn)內(nèi)存映射(根據(jù)ServiceManager進(jìn)程里的Service信息找到對(duì)應(yīng)的Server進(jìn)程,實(shí)現(xiàn)內(nèi)核緩存區(qū) 和 Server 進(jìn)程用戶空間地址 同時(shí)映射到 同1個(gè)接收緩存區(qū)中)
Client進(jìn)程:將參數(shù)寫(xiě)入Parcel對(duì)象中打包(序列化)侧馅,通過(guò)BinderProxy的transact()將數(shù)據(jù)發(fā)送內(nèi)核緩存區(qū)(此時(shí)Binder驅(qū)動(dòng)由于存在映射關(guān)系相當(dāng)于給到Binder驅(qū)動(dòng))危尿,等待Server返回replay,并掛起Client當(dāng)前線程
Sever進(jìn)程:收到驅(qū)動(dòng)通知后馁痴,調(diào)用Binder對(duì)象的onTransact()將Parcel解包(反序列化)并執(zhí)行要client要調(diào)用目標(biāo)方法并返回replay谊娇,隨后把replay返回給Client,此時(shí)Client線程被喚醒 完成了一次服務(wù)的使用
流程如下圖
獲取服務(wù)
Client獲取服務(wù)時(shí)罗晕,會(huì)去ServiceManager獲取服務(wù)名對(duì)應(yīng)的IBinder济欢,隨后通過(guò)asInterface獲取服務(wù)
static publicINotificationManagergetService()
? ? {
if (sService!= null) {
returnsService;
? ? ? ? }
IBinderb=ServiceManager.getService("notification");
sService=INotificationManager.Stub.asInterface(b);
returnsService;
? ? }
而asInterface會(huì)去獲本地對(duì)象,否則就給到Binder代理類
? ? public static BookManager asInterface(IBinder binder){
? ? ? ? if (binder == null)
? ? ? ? ? ? return null;
? ? ? ? //尋找Binder本地對(duì)象
IInterface iin = binder.queryLocalInterface(DESCRIPTOR);
? ? ? ? if (iin != null && iin instanceof BookManager){
? ? ? ? ? ? return (BookManager) iin;
? ? ? ? }
? ? ? ? return new Proxy(binder);
/**
? ? ? ? * 如果server端(service所在的進(jìn)程)和client端處于同一個(gè)進(jìn)程 那么此時(shí)不需要跨進(jìn)程通信 直接調(diào)用Stub的方法即可(Stub實(shí)例化時(shí)已經(jīng)實(shí)現(xiàn)了具體的方法)
? ? ? ? * 反之攀例,則返回proxy對(duì)象? 從構(gòu)造方法看 該對(duì)象持有著遠(yuǎn)程Binder引用? 如果調(diào)用Stub.proxy接口 那么將會(huì)是IPC調(diào)用? 即通過(guò)transact方法與服務(wù)端進(jìn)行通信
? ? ? ? * 從字面意思看stub是服務(wù)端實(shí)現(xiàn)的存根(Stub工作在server進(jìn)程)船逮,
? ? ? ? * proxy則是stub的代理 proxy工作在client進(jìn)程
? ? ? ? */
? ? }
使用服務(wù)
步驟1: Client進(jìn)程 將參數(shù)(整數(shù)a和b)發(fā)送到Server進(jìn)程
// 1. Client進(jìn)程 將需要傳送的數(shù)據(jù)寫(xiě)入到Parcel對(duì)象中
// data = 數(shù)據(jù) = 目標(biāo)方法的參數(shù)(Client進(jìn)程傳進(jìn)來(lái)的,此處就是整數(shù)a和b) + IInterface接口對(duì)象的標(biāo)識(shí)符descriptor
? android.os.Parcel data = android.os.Parcel.obtain();
? data.writeInt(a);
? data.writeInt(b);
? data.writeInterfaceToken("add two int");粤铭;
? // 方法對(duì)象標(biāo)識(shí)符讓Server進(jìn)程在Binder對(duì)象中根據(jù)"add two int"通過(guò)queryLocalIInterface()查找相應(yīng)的IInterface對(duì)象(即Server創(chuàng)建的plus)挖胃,Client進(jìn)程需要調(diào)用的相加方法就在該對(duì)象中
? android.os.Parcel reply = android.os.Parcel.obtain();
? // reply:目標(biāo)方法執(zhí)行后的結(jié)果(此處是相加后的結(jié)果)
// 2. 通過(guò) 調(diào)用代理對(duì)象的transact() 將 上述數(shù)據(jù)發(fā)送到Binder驅(qū)動(dòng)
? binderproxy.transact(Stub.add, data, reply, 0)
? // 參數(shù)說(shuō)明:
? ? // 1. Stub.add:目標(biāo)方法的標(biāo)識(shí)符(Client進(jìn)程 和 Server進(jìn)程 自身約定,可為任意)
? ? // 2. data :上述的Parcel對(duì)象
? ? // 3. reply:返回結(jié)果
? ? // 0:可不管
// 注:在發(fā)送數(shù)據(jù)后梆惯,Client進(jìn)程的該線程會(huì)暫時(shí)被掛起
// 所以酱鸭,若Server進(jìn)程執(zhí)行的耗時(shí)操作,請(qǐng)不要使用主線程垛吗,以防止ANR
// 3. Binder驅(qū)動(dòng)根據(jù) 代理對(duì)象 找到對(duì)應(yīng)的真身Binder對(duì)象所在的Server 進(jìn)程(系統(tǒng)自動(dòng)執(zhí)行)
// 4. Binder驅(qū)動(dòng)把 數(shù)據(jù) 發(fā)送到Server 進(jìn)程中凹髓,并通知Server 進(jìn)程執(zhí)行解包(系統(tǒng)自動(dòng)執(zhí)行)
步驟2:Server進(jìn)程根據(jù)Client進(jìn)要求 調(diào)用 目標(biāo)方法(即加法函數(shù))
// 1. 收到Binder驅(qū)動(dòng)通知后,Server 進(jìn)程通過(guò)回調(diào)Binder對(duì)象onTransact()進(jìn)行數(shù)據(jù)解包 & 調(diào)用目標(biāo)方法
? public class Stub extends Binder {
? ? ? ? ? // 復(fù)寫(xiě)onTransact()
? ? ? ? ? @Override
? ? ? ? ? boolean onTransact(int code, Parcel data, Parcel reply, int flags){
? ? ? ? ? // code即在transact()中約定的目標(biāo)方法的標(biāo)識(shí)符
? ? ? ? ? switch (code) {
? ? ? ? ? ? ? ? case Stub.add: {
? ? ? ? ? ? ? ? ? // a. 解包Parcel中的數(shù)據(jù)
? ? ? ? ? ? ? ? ? ? ? data.enforceInterface("add two int");
? ? ? ? ? ? ? ? ? ? ? ? // a1. 解析目標(biāo)方法對(duì)象的標(biāo)識(shí)符
? ? ? ? ? ? ? ? ? ? ? int? arg0? = data.readInt();
? ? ? ? ? ? ? ? ? ? ? int? arg1? = data.readInt();
? ? ? ? ? ? ? ? ? ? ? // a2. 獲得目標(biāo)方法的參數(shù)
? ? ? ? ? ? ? ? ? ? ? // b. 根據(jù)"add two int"通過(guò)queryLocalIInterface()獲取相應(yīng)的IInterface對(duì)象(即Server創(chuàng)建的plus)的引用怯屉,通過(guò)該對(duì)象引用調(diào)用方法
? ? ? ? ? ? ? ? ? ? ? int? result = this.queryLocalIInterface("add two int") .add( arg0,? arg1);
? ? ? ? ? ? ? ? ? ? ? ? // c. 將計(jì)算結(jié)果寫(xiě)入到reply
? ? ? ? ? ? ? ? ? ? ? ? reply.writeInt(result);
? ? ? ? ? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? ? ? ? }
? ? ? ? ? }
? ? ? return super.onTransact(code, data, reply, flags);
? ? ? // 2. 將結(jié)算結(jié)果返回 到Binder驅(qū)動(dòng)
步驟3:Server進(jìn)程 將目標(biāo)方法的結(jié)果(即加法后的結(jié)果)返回給Client進(jìn)程
? // 1. Binder驅(qū)動(dòng)根據(jù) 代理對(duì)象 沿原路 將結(jié)果返回 并通知Client進(jìn)程獲取返回結(jié)果
? // 2. 通過(guò)代理對(duì)象 接收結(jié)果(之前被掛起的線程被喚醒)
? ? binderproxy.transact(Stub.ADD, data, reply, 0)蔚舀;
? ? reply.readException();饵沧;
? ? result = reply.readInt();
? ? ? ? ? }
}
Binder驅(qū)動(dòng)加載過(guò)程中有哪些重要步驟
優(yōu)點(diǎn)
對(duì)比 Linux (Android基于Linux)上的其他進(jìn)程通信方式(管道赌躺、消息隊(duì)列狼牺、共享內(nèi)存、
信號(hào)量礼患、Socket)是钥,Binder 機(jī)制的優(yōu)點(diǎn)有:
IBinder、BpBinder缅叠、BBinder悄泥、Stub、IInterface
名稱描述
IBinder一個(gè)接口類肤粱,代表了一種跨進(jìn)程通訊的能力弹囚,實(shí)現(xiàn)這個(gè)接口,這個(gè)對(duì)象就能跨進(jìn)程傳輸
BpBinder代理Binder狼犯,當(dāng)clicnt調(diào)用service后余寥,會(huì)得到一個(gè)BpXXXService,它將str參數(shù)打包到Parcel中悯森,然后調(diào)用remote()->transact,remote()返回的就是一個(gè)BpBinder,所以實(shí)際上調(diào)用的是BpBinder的transact
BBinder服務(wù)端Binder绪撵,當(dāng)Service接收到client請(qǐng)求后瓢姻,會(huì)調(diào)用BBinder的taransact方法會(huì)調(diào)回子類實(shí)現(xiàn)的虛擬方法onTransact,該方法實(shí)現(xiàn)是在BnXXXservice中實(shí)現(xiàn)音诈,所以BBinder就是接受傳遞過(guò)來(lái)的信息幻碱,解包數(shù)據(jù),并調(diào)用XXXservice真正的實(shí)現(xiàn)
IInerfaceAIDL文件中定義的接口细溅,表示Server進(jìn)程對(duì)象具備哪些能力
Stub一個(gè)抽象類褥傍,其中由asInterface和onTransact關(guān)鍵函數(shù)組成
其中asInterface 傳入binder對(duì)象,如果同一進(jìn)程
序列化與反序列化
為什么要序列化與反序列化喇聊?
因?yàn)閷?duì)象內(nèi)的各種數(shù)據(jù)不能直接跨進(jìn)程傳遞恍风,所以需要將java對(duì)象轉(zhuǎn)換成字節(jié)序列(序列化)等到了接收方便要從字節(jié)序列中恢復(fù)出Java對(duì)象(反序列化)
序列化有哪些好處?
實(shí)現(xiàn)了數(shù)據(jù)的持久化誓篱,通過(guò)序列化可以把數(shù)據(jù)永久的保存在硬盤(pán)上
Serializable與Parcelable的區(qū)別
SerializableParcelable
原理通過(guò)I/O讀寫(xiě)存儲(chǔ)再磁盤(pán)上朋贬,同各國(guó)反射解析出對(duì)象描述、屬性的描述窜骄,以HandleTable 來(lái)解析存儲(chǔ)信息锦募,然后生成二進(jìn)制、存儲(chǔ)邻遏、傳輸主要用于Binder傳輸糠亩,將數(shù)據(jù)寫(xiě)入共享內(nèi)存虐骑,其他進(jìn)程通過(guò)Parcel可以從這塊共享內(nèi)存中讀出字節(jié)流
存儲(chǔ)介質(zhì)使用I/O讀寫(xiě)存儲(chǔ)在硬盤(pán)上直接在內(nèi)存中讀取,內(nèi)存中的結(jié)構(gòu)是一塊連續(xù)的內(nèi)存赎线,會(huì)根據(jù)需要的內(nèi)存大小自動(dòng)擴(kuò)展
來(lái)源JavaAndroid
效率通過(guò)反射富弦,需要大量的I/O操作自己實(shí)現(xiàn)封送和解封,效率更快
使用序列化:
要?jiǎng)?chuàng)建某些OutputStream對(duì)象
將其封裝到ObjectOutputStream對(duì)象內(nèi)
此后只需調(diào)用writeObject()即可完成對(duì)象的序列化
忘記關(guān)閉資源:objectOutputStream.close(), outputStream .close();
反序列化:
通過(guò)readObject()獲取資源
通過(guò)IPC傳遞的對(duì)象都要繼承于Parcelable接口氛驮,并實(shí)現(xiàn) writeToParcel腕柜,它會(huì)獲取對(duì)象的當(dāng)前狀態(tài)并將其寫(xiě)入 Parcel。為類添加CREATOR靜態(tài)字段矫废,該字段是實(shí)現(xiàn)Parcelable.Creator接口的對(duì)象盏缤。最后,創(chuàng)建聲明 Parcelable 類的.aidl文件
Binder中ServiceManager的作用
ServiceManage使用客戶端可以獲取binder實(shí)例對(duì)象的引用
AIDL
什么是AIDL
AIDL是Android提供的接口定義語(yǔ)言蓖扑,簡(jiǎn)化Binder的使用唉铜,會(huì)生成一個(gè)服務(wù)代理端對(duì)象的代理類,可以理解成服務(wù)端代碼律杠,與Client客戶端中要鏈接的binder相關(guān)如服務(wù)連接asInterface潭流、transact、序列化柜去,以及支持IPC的目標(biāo)方法等
使用步驟
(1)建立aidl的服務(wù)方:
a.建立一個(gè)含有activity的service,其實(shí)也可以直接建立一個(gè)service
b.建立aidl文件灰嫉,聲明接口以及方法,建立完成后ADT會(huì)自動(dòng)生成一個(gè)aild接口的實(shí)現(xiàn)java文件,在gen下面.
c.建立service繼承類嗓奢,內(nèi)部實(shí)現(xiàn)aidl的stub.且在onbind中進(jìn)行返回.
b.AndroidManifest.xml文件中配置AIDL服務(wù)讼撒,需要注意的是<action>標(biāo)簽中android:name的屬性值就是客戶端要引用該服務(wù)的ID,也就是Intent類的參數(shù)值股耽。
(2)建立客戶斷完成跨進(jìn)程的調(diào)用.
a.建立android project內(nèi)部會(huì)自動(dòng)創(chuàng)建一個(gè)activity根盒,在activity中bind服務(wù).bind服務(wù)成功后,onServiceConnected將會(huì)回調(diào)返回給service給定的ibind.
b.onServiceConnected根據(jù)返回的內(nèi)容物蝙,實(shí)現(xiàn)跨進(jìn)程的調(diào)用.
參考文章:
https://blog.csdn.net/carson_ho/article/details/73560642