Binder的原理
要想了解AIDL
就需要先了解Binder
的原理斗搞,所以這里先說一下Binder
原理逗堵,Binder
的原理大概是這樣:
服務(wù)器端:當(dāng)我們在服務(wù)端創(chuàng)建好了一個Binder對象后,內(nèi)部就會開啟一個線程用于接收binder驅(qū)動發(fā)送的消息刽漂,收到消息后會執(zhí)行相關(guān)的服務(wù)器代碼渴肉。
Binder驅(qū)動:當(dāng)服務(wù)端成功創(chuàng)建一個Binder對象后,Binder驅(qū)動也會創(chuàng)建一個mRemote對象爽冕,該對象的類型也是Binder類仇祭,客戶就可以借助這個mRemote對象來訪問遠(yuǎn)程服務(wù),注意這里是借助颈畸,真正調(diào)用的時候需要將這個轉(zhuǎn)換成對應(yīng)的對象乌奇,比如使用AIDL
的時候就要轉(zhuǎn)換成AIDL
對象。
客戶端:客戶端要想訪問Binder的遠(yuǎn)程服務(wù)眯娱,就必須獲取遠(yuǎn)程服務(wù)的Binder對象在binder驅(qū)動層對應(yīng)的mRemote引用礁苗。當(dāng)獲取到mRemote對象的引用后,就可以調(diào)用相應(yīng)Binder對象的暴露給客戶端的方法(如果有方法的話)徙缴。
AIDL
AIDL
的本質(zhì)其實(shí)就是系統(tǒng)為我們提供了一種快速實(shí)現(xiàn)Binder
的工具试伙,我們完全可以不用AIDL
,自己去寫代碼實(shí)現(xiàn)Binder
于样,但是當(dāng)你寫出來的時候會發(fā)現(xiàn)其實(shí)和AIDL
自動生成的代碼一模一樣疏叨。我們接下來來分析一下原理,因?yàn)?code>AIDL的實(shí)現(xiàn)其實(shí)就是快速實(shí)現(xiàn)Binder
穿剖,所以原理自然離不開Binder
蚤蔓。但是在分析原理之前,我們先將系統(tǒng)根據(jù)我們定義的AIDL
文件自動生成的java文件分析一下糊余。比較重要的就是Stub
和它的內(nèi)部代理類Proxy
秀又。我們說一下重要的方法:
asInterface(android.os.IBinder obj)
用于將服務(wù)器的Binder對象轉(zhuǎn)換成客戶端所需的AIDL
接口類型的對象,這種轉(zhuǎn)換過程是區(qū)分進(jìn)程的贬芥,如果客戶端和服務(wù)端位于統(tǒng)一進(jìn)程吐辙,那么返回服務(wù)器的Stub
對象本身,否則返回的是系統(tǒng)封裝后的Stub.proxy
對象蘸劈。
onTransact(int code,android.os.Parcel data,android.os.Parcel reply,int flags)
這個方法運(yùn)行在服務(wù)端的Binder線程池中昏苏,當(dāng)客戶端發(fā)起跨進(jìn)程請求時,遠(yuǎn)程請求會通過系統(tǒng)底層封裝后交由服務(wù)端 的onTransact
方法來處理。這個方法有四個參數(shù)捷雕,分別是code
椒丧,data
,reply
救巷,flags
.code是確定客戶端請求的方法是哪個壶熏,data是目標(biāo)方法所需的參數(shù),reply是服務(wù)器端執(zhí)行完后的返回值浦译。如果這個方法返回false
棒假,那么客戶端的請求會失敗。
Proxy#getBookList
這里的getBookList
方法就是在自定義的AIDL
文件中定義的方法精盅,這個方法運(yùn)行在客戶端帽哑,當(dāng)客戶端遠(yuǎn)程調(diào)用此方法的時候,內(nèi)部實(shí)現(xiàn)是這樣的:首先在代理類中創(chuàng)建該方法所需要的輸入型Parcel對象_data叹俏,輸出型Parcel對象_reply和返回值對象List;然后把該方法的參數(shù)信息寫入_data
中妻枕,接著mRemote
調(diào)用transact
方法來發(fā)起RPC(遠(yuǎn)程過程調(diào)用)請求, 同時當(dāng)前線程掛起粘驰,然后服務(wù)端的onTransact
方法會被調(diào)用屡谐,直到RPC返回后,當(dāng)前線程繼續(xù)執(zhí)行蝌数,并從_reply
中取出RPC過程的返回結(jié)果并返回(如果有返回值的話)愕掏,之前創(chuàng)建的參數(shù)其實(shí)就是onTransact()
方法需要的參數(shù)。
說完了重要方法顶伞,接下來分析AIDL
原理:
服務(wù)端:因?yàn)橐獙?shí)現(xiàn)Binder
饵撑,必須在服務(wù)器端創(chuàng)建一個Binder
對象,如何創(chuàng)建呢唆貌?就是newAIDL
接口中的Stub
內(nèi)部類滑潘,代碼示例如:
Binder mBinder=new IBookManager.Stub(){接口方法實(shí)現(xiàn)}
其中IBookManager
是系統(tǒng)根據(jù)我們自己定義的IBookManager.AIDL
所生成的類。
Binder驅(qū)動:在AIDL
中挠锥,Binder驅(qū)動其實(shí)就是Service众羡。
客戶端:要實(shí)現(xiàn)客戶端跨進(jìn)程和服務(wù)端通信侨赡,必須獲得服務(wù)端的Binder
對象在binder驅(qū)動層對應(yīng)的mRemote引用蓖租,如何獲得呢?首先綁定遠(yuǎn)程服務(wù)羊壹,綁定成功后的ServiceConnection
中的IBinder service
其實(shí)就是mRemote引用
蓖宦,但是因?yàn)槭鞘褂?code>AIDL方式,所以需要在客戶端中調(diào)用IBookManager.Stub.asInterface(android.os.IBinder obj)
方法將服務(wù)器返回的Binder對象轉(zhuǎn)換成AIDL
接口油猫,然后就可以通過這個接口去調(diào)用服務(wù)器的遠(yuǎn)程方法了稠茂。
根據(jù)原理,我們得出AIDL
的使用流程,其實(shí)很簡單睬关,大致就是在服務(wù)端創(chuàng)建一個Service
诱担,然后創(chuàng)建一個Binder
對象,最后在客戶端得到這個Binder
對象电爹。
AIDL
使用流程:
先建立AIDL
蔫仙,如果在你建立的AIDL
接口中,有自定義的類丐箩,那么摇邦,也需要建立這個類的AIDL,并且名字要完全相同屎勘。同時在使用的時候施籍,一定要顯示的導(dǎo)入這個類。接下來的流程就是跟Binder的一樣了概漱。
服務(wù)器端:創(chuàng)建Binder
對象丑慎,并且實(shí)現(xiàn)接口中的方法。
客戶端:綁定service瓤摧,得到Binder
對象在驅(qū)動層對應(yīng)的mRemote引用立哑。
重點(diǎn)
1.當(dāng)你在客戶端調(diào)用服務(wù)器的方法的時候,其實(shí)是通過代理去訪問姻灶,詳情可以看上面的重點(diǎn)方法介紹里的Proxy#getBookList
铛绰,所以你在客戶端連續(xù)調(diào)用兩次服務(wù)器的同一個方法的時候,比如产喉,這里的getBookList捂掰,你會發(fā)現(xiàn),里面的對象都不一樣曾沈。因?yàn)槊看卧谡{(diào)用方法的時候这嚣,在代理類中都會創(chuàng)建該方法所需要的參數(shù)對象,所以里面的對象會變化塞俱。
2.AIDL
中無法使用普通的接口姐帚,只能使用AIDL
接口,并且實(shí)現(xiàn)AIDL
接口的時候不能用implements
障涯,因?yàn)樾枰獙?shí)現(xiàn)的接口其實(shí)是自定義接口.Stub
罐旗,而不是自己定義的那個接口。使用implements
無法實(shí)現(xiàn)唯蝶。
3.解注冊的時候需要使用到RemoteCallbackList
九秀,需要注意的是這個類的beginBroadcast()
和finishBroadcast()
一定要配對使用,否則會出現(xiàn)異常java.lang.IllegalStateException: beginBroadcast() called while already in a broadcast
粘我,特別是在使用for
循環(huán)的時候鼓蜒。
4.對于AIDL
中的in
,out
,inout
這里就直接附上一篇別人寫的博客都弹,這篇博客講的很詳細(xì)娇豫,而且我也贊同他的觀點(diǎn),紙上得來終覺淺畅厢,絕知此事要躬行锤躁。
5.當(dāng)使用客戶端調(diào)用服務(wù)器的方法的時候,被調(diào)用的方法運(yùn)行在服務(wù)器的Binder線程池中或详,同時客戶端會被掛起系羞,如果此時服務(wù)端方法執(zhí)行耗時的話,就會導(dǎo)致客戶端線程長時間阻塞霸琴,如果客戶端線程是UI線程的話椒振,就會導(dǎo)致客戶端ANR,注意的是onServiceConnected(ComponentName name, IBinder service)
和onServiceDisconnected(ComponentName name)
都運(yùn)行在UI線程梧乘,所以不能在這里調(diào)用服務(wù)端耗時的方法澎迎。同理,對于服務(wù)端調(diào)用客戶端的方法的情況选调,比如服務(wù)端調(diào)用客戶端的listener
中的方法的時候也是一樣夹供。即服務(wù)端掛起,方法運(yùn)行在客戶端的Binder線程池中仁堪。
6.當(dāng)服務(wù)端因?yàn)槟撤N異常原因停止哮洽,我們需要重新啟動服務(wù)端,這里有兩種方式弦聂,因?yàn)?code>AIDL的底層是Binder
鸟辅,所以可以使用Binder
的linkToDeath
和unlinkToDeath
方法。還有一種方式是在onServiceDisconnected(ComponentName name)
重新綁定莺葫。這兩個區(qū)別就是第二種方式可以訪問UI匪凉,第一種不行,因?yàn)橄裰罢f的捺檬,onServiceDisconnected(ComponentName name)
是運(yùn)行在UI線程里的再层。而第一種方式使用的時候需要設(shè)置一個IBinder.DeathRecipient
接口用于接收服務(wù)端binder
因?yàn)樘厥庠蛳У耐ㄖ?dāng)收到通知的時候就會回調(diào)binderDied()
方法堡纬,我們在這里unlinkToDeath
并且重新綁定service聂受。而這個binderDied()
方法是運(yùn)行在客戶端的Binder
線程池中的。
Messenger的原理及使用
Messenger大致的原理是這樣的隐轩,因?yàn)镸essenger的底層還是AIDL
饺饭,所以,原理和AIDL
差不多职车。
服務(wù)器:首先需要在服務(wù)器創(chuàng)建Binder對象,如何創(chuàng)建呢?通過Messenger
來創(chuàng)建悴灵,所以我們需要先構(gòu)造Messenger
對象,對于Messenger
的構(gòu)造方法有兩種扛芽,如下:
public Messenger(IBinder target) {
mTarget = IMessenger.Stub.asInterface(target);
}
public Messenger(Handler target) {
mTarget = target.getIMessenger();
}
所以我們需要先構(gòu)造一個Handler,這個Handler的作用其實(shí)就是處理消息积瞒。然后我們再通過這個Handler
來構(gòu)造Messenger
對象川尖,這個Messenger對象其實(shí)就是將客戶端發(fā)送來的消息傳遞給Handler來處理,然后我們需要得到Binder對象茫孔,通過在Service
的onBind
方法中return Messenger.getBinder()
叮喳,這樣就得到了Binder
對象。
Binder驅(qū)動:跟AIDL
一樣缰贝,還是Service
馍悟。
客戶端:也是需要得到服務(wù)端的Binder
對象在binder驅(qū)動層對應(yīng)的mRemote引用,獲得的方式是將ServiceConnection
中的IBinder service
當(dāng)做參數(shù)傳入Messenger的構(gòu)造函數(shù)中剩晴,如:
Messenger mService=new Messenger(service)锣咒;
然后就可以用mService.send(msg)
給服務(wù)器發(fā)消息。實(shí)現(xiàn)跨進(jìn)程通信赞弥。
因?yàn)檫@里是借助Messenger
毅整,所以無法調(diào)用服務(wù)器端的方法,只能通過message來傳遞消息绽左。而當(dāng)服務(wù)器需要回應(yīng)客戶端的時候悼嫉,就需要客戶端提供一個Messenger
,然后服務(wù)器得到這個Messenger
拼窥,因?yàn)樵诰拖窨蛻舳讼蚍?wù)端發(fā)送請求的時候承粤,也是服務(wù)器提供一個Messenger
,然后客戶端得到這個Messenger
闯团。那么如何實(shí)現(xiàn)呢辛臊?因?yàn)榭蛻舳撕头?wù)器已經(jīng)建立了連接,所以只需要在客戶端發(fā)送消息的時候房交,通過消息的replyTo
參數(shù)向服務(wù)器傳入一個Messenger
彻舰,然后服務(wù)器在接收到客戶端的消息的時候得到通過message
的replyTo
參數(shù)得到這個Messenger
,然后利用這個向客戶端發(fā)送消息就可以了候味。主要代碼如下:
在客戶端發(fā)送消息給服務(wù)器的時候:
message.replyTo=clientMessenger;
服務(wù)器接收消息的時候
Messenger clientMessenger=msg.replyTo;
這樣就在服務(wù)器端得到了客戶端的Messenger
刃唤,然后在服務(wù)器端通過clientMessenger.send(message);
就向客戶端發(fā)送了消息。
重點(diǎn)
1.對于使用Messenger
而言白群,底層其實(shí)是AIDL
尚胞,但是沒有AIDL
靈活,因?yàn)檫@是借助Messenger
來發(fā)送消息從而進(jìn)行消息的傳遞帜慢,不能直接調(diào)用服務(wù)端的方法笼裳,而使用AIDL
是直接可以調(diào)用服務(wù)端的方法唯卖。
2.對于服務(wù)端的Messenger
的作用是將客戶端傳遞的消息傳遞給Handler
來處理,而客戶端的是發(fā)送消息給服務(wù)端躬柬。
3.Messenger
是以串行的方式處理客戶端發(fā)來的消息拜轨,當(dāng)消息多的時候就就不合適了。而AIDL
是可以并發(fā)處理客戶端傳來的消息允青。