AIDL整理

  • 何為AIDL

Android Interface Definition Language即Android接口定義語言

我們知道亿蒸,因?yàn)橛脩粜枰袈瑁珹ndroid系統(tǒng)中可以同時(shí)運(yùn)行著多個(gè)進(jìn)程,為了進(jìn)程間能夠正常的運(yùn)行贸桶,進(jìn)程之間必須保持獨(dú)立互不干擾刺桃,這就是進(jìn)程隔離。但是很多時(shí)候我們又不得不和另一個(gè)進(jìn)程通信监透,那么這種進(jìn)程間的通信就叫做IPC(Inter-Process Communication)。實(shí)現(xiàn)IPC的方式有很多種航唆,像管道胀蛮、信號(hào)、消息隊(duì)列糯钙、共享內(nèi)存醇滥、Socket等,RPC(Reomote Procedure Call超营,譯稱遠(yuǎn)程過程調(diào)用)也是IPC的一種方式,AIDL使用的就是這種方式阅虫。

  • AIDL原理簡(jiǎn)述

binder-model

圖片來源:Binder學(xué)習(xí)指南 (ps:這篇文章對(duì)Binder機(jī)制寫的不錯(cuò)演闭,值得一看)

簡(jiǎn)單來說,就是方法提供方稱為server颓帝,方法調(diào)用方稱為client米碰,client和server之間的交互就是IPC。首先server注冊(cè)到一個(gè)稱為ServiceManager的地方购城,client調(diào)用時(shí)會(huì)去查詢?cè)搒erver是否已經(jīng)注冊(cè)吕座,若已注冊(cè)則取到server的代理對(duì)象并執(zhí)行要調(diào)用的方法,對(duì)于調(diào)用方來說就和調(diào)用自己進(jìn)程中的方法一樣瘪板,但是背后的工作確是由Binder驅(qū)動(dòng)轉(zhuǎn)交給真正擁有該方法的server去執(zhí)行并把結(jié)果返回給調(diào)用方client吴趴。

  • AIDL是如何做的

我們還是從使用開始,一步步揭開AIDL的神秘面紗

  • 創(chuàng)建AIDL文件

在src/main下新建文件夾aidl侮攀,在其下新建一個(gè).aidl文件锣枝,如果你是使用studio的創(chuàng)建AIDL文件功能選項(xiàng)創(chuàng)建的aidl文件,則會(huì)看到自動(dòng)生成了一個(gè)basicTypes方法:

image-20200710151840622

這個(gè)方法里面的參數(shù)就是aidl支持的基本類型(你可以把這個(gè)方法刪掉也不會(huì)有什么問題)兰英,除此之外撇叁,最好都需要import語句導(dǎo)入(List也可以不用導(dǎo)包,但是是建立在存在jdk的基礎(chǔ)上)畦贸,所以最規(guī)范的做法就是除基本類型之外的類型都要導(dǎo)入陨闹,和java語法一樣,導(dǎo)入為止如下圖:

image-20200710152247796

當(dāng)需要自定義的數(shù)據(jù)類型時(shí),首先要求這個(gè)自定義類型是parcelable類型趋厉,并實(shí)現(xiàn)Parcelable需要的所有東西寨闹,這里值得注意的是,因?yàn)锳IDL規(guī)定除了基本數(shù)據(jù)類型之外觅廓,其他所有類型都需要定向tag顯示聲明鼻忠,有in、out杈绸、inout三種帖蔓,基本數(shù)據(jù)類型默認(rèn)都是in(只能是in),后面會(huì)講到其作用瞳脓,這里我們只要記得如果要支持out或者inout定向的話要手動(dòng)添加readFromParcel()方法塑娇,因?yàn)檫@個(gè)方法Parcelable接口中沒有,需要自行添加:

image-20200710164356911

其次需要定義此類的aidl文件劫侧,這個(gè)aidl文件因?yàn)椴皇墙涌谒圆粫?huì)被自動(dòng)生成(generated):

image-20200710153904493

特別注意: Demo02.aidl的包名要和Demo02.java的包名完全一致B癯辍!

然后在要使用這個(gè)類的aidl文件中引入:

image-20200710154018178

可以看到pringtList和addDemo02方法中的參數(shù)都不是基本類型烧栋,所以前面都必須有一個(gè)定向tag修飾(這里是in)写妥,那么in、out审姓、inout的意義是什么呢珍特?簡(jiǎn)單來說,根據(jù)前面我們提到的進(jìn)程間通信的機(jī)制魔吐,通信的雙方是client和server扎筒,當(dāng)我們從client傳遞一個(gè)引用類型的數(shù)據(jù)給server端的時(shí)候,如果server端修改了數(shù)據(jù)酬姆,若定向是in嗜桌,則client的數(shù)據(jù)不會(huì)被改變,若是out或者是inout則client端的數(shù)據(jù)也會(huì)跟著改變(比如說你調(diào)用test(a)辞色,如果服務(wù)端處理時(shí)a的某些屬性值變了骨宠,那客戶端的a對(duì)象的對(duì)應(yīng)屬性值也會(huì)跟著改變);同理如果定向修飾為out相满,則server端不會(huì)收到client發(fā)送的引用數(shù)據(jù)類型(比如說你調(diào)用test(a)诱篷,服務(wù)端是不會(huì)使用這個(gè)a的,它只會(huì)重新創(chuàng)建一個(gè)同樣類型的新對(duì)象)雳灵。這里可以發(fā)現(xiàn)棕所,只有引用變量才可能被改變,因?yàn)橐脭?shù)據(jù)類型有地址指向悯辙,所以這也就是為什么基本數(shù)據(jù)類型默認(rèn)是in定向修飾符琳省,而且只能是in迎吵,這里其實(shí)有一個(gè)readFromParcel的操作,后面會(huì)看到针贬。關(guān)于定向tag击费,這篇文章講的很詳細(xì):你真的理解AIDL中的in、out桦他、inout么蔫巩?

好了到現(xiàn)在為止我們已經(jīng)做好了所有準(zhǔn)備工作,現(xiàn)在只需要點(diǎn)擊一下studio的rebuild project就會(huì)看到在對(duì)應(yīng)文件夾中自動(dòng)生成的java文件快压,我猜測(cè)studio版本和gradle版本不同圆仔,可能生成的目標(biāo)目錄也會(huì)不一樣,所以不要死記這個(gè)路徑蔫劣,但肯定會(huì)在build/generated目錄下:

image-20200710160104208

下面我們?nèi)タ纯唇o我們自動(dòng)生成的java接口類:

1594368452548

可以看到我們的接口繼承了一個(gè)android.os.IInterface接口:

image-20200710161039214

沒什么特別含義坪郭,就是為了轉(zhuǎn)化成IBinder類型的。

再來看內(nèi)部脉幢,除了簡(jiǎn)單的聲明了我們aidl中定義的接口方法之外歪沃,靜態(tài)內(nèi)部抽象類Stub是自動(dòng)幫我們創(chuàng)建的內(nèi)容,它繼承了android.os.Binder并實(shí)現(xiàn)了外部的AIDL接口嫌松,它是AIDL的核心沪曙。

  • 使用AIDL

我們使用常見的Activity和Service的通信來梳理AIDL的工作,首先創(chuàng)建Service:

image

注意這里的Service可以被多個(gè)主題綁定萎羔,所以這里的方法要考慮線程安全珊蟀,其次,因?yàn)閎inder是匿名類實(shí)現(xiàn)外驱,同時(shí),它又是私有變量只通過onBind方法暴露腻窒,所以這里自定義的方法不能夠被外部調(diào)用昵宇。

再來看一下client端關(guān)鍵代碼:

image-20200712091343773

mAidlService是一個(gè)AidlTest02類型的變量,在service綁定成功之后儿子,通過AidlTest02.Stub.asInterface(IBinder)方法賦值瓦哎,我們看看這個(gè)方法:

image-20200712092257714

這里出現(xiàn)了兩個(gè)return分支,本地調(diào)用返回和遠(yuǎn)程調(diào)用返回柔逼。

  • 本地調(diào)用

    如果在單個(gè)App的內(nèi)部通過bindService實(shí)現(xiàn)Activity和Service交互蒋譬,就會(huì)走queryLocalInterface本地獲取,此時(shí)這里的obj就是我們的Service里面定義的AidlTest02.Stub的匿名類對(duì)象binder愉适,通過onBind方法傳到了這里犯助,如果不為空則調(diào)用它的queryLocalInterface方法去獲取一個(gè)IInterface類型的對(duì)象,這是IBinder接口的的方法维咸,因?yàn)槲覀兊腟tub繼承了Binder剂买,所以我們直接去Binder下面去找這個(gè)方法的實(shí)現(xiàn):
    image-20200712092830056

    很簡(jiǎn)單惠爽,如果mDescriptor比對(duì)成功則會(huì)返回一個(gè)IInterface類型的mOwner,那這兩個(gè)值是何時(shí)賦值的呢瞬哼?還記得我們前面截圖中有一個(gè)Stub的構(gòu)造方法嗎婚肆,這個(gè)構(gòu)造方法是在Service里定義binder匿名類的時(shí)候調(diào)用的,構(gòu)造方法里調(diào)用了this.attachInterface(this,DESCRIPTOR):
    image-20200712093553659

    DESCRIPTOR是Stub內(nèi)部定義的常量坐慰,owner參數(shù)就是Stub本身较性,因?yàn)镾tub實(shí)現(xiàn)了AidlTest02接口,AidlTest02又實(shí)現(xiàn)了IInterface接口结胀。

    另外赞咙,因?yàn)槭侵苯邮褂帽镜氐腷inder對(duì)象,并沒有走Proxy邏輯把跨,所以本地調(diào)用時(shí)定向tag是無效的人弓。

  • 遠(yuǎn)程調(diào)用

    因?yàn)槭茿ndroid間的系統(tǒng)進(jìn)程間通信,所以不是傳統(tǒng)意義上以網(wǎng)絡(luò)為傳輸介質(zhì)的RPC着逐,這里的遠(yuǎn)程調(diào)用就是App之間的進(jìn)程通信崔赌。我們需要?jiǎng)?chuàng)建兩個(gè)App(一個(gè)項(xiàng)目中的兩個(gè)Module即可),兩個(gè)App分別是client端和service端耸别,都需要共同的aidl文件健芭,而且他們的包名要嚴(yán)格相同 。

    這里只貼一下另一個(gè)Module中Service相關(guān)代碼:

    image-20200712200841772
    image-20200712200752483

    下面部分截圖還是上面的本地調(diào)用的秀姐,不過原理是一樣的慈迈,沒什么影響,AidlAutoCreateTest03是我新建的遠(yuǎn)程服務(wù)端aidl文件省有,這里有三個(gè)方法分別使用了in痒留、out和inout定向,下面有一些表現(xiàn)出關(guān)于in蠢沿、out和inout的不一樣的地方我會(huì)用這個(gè)新的遠(yuǎn)程代理服務(wù)的一些源碼截圖說明伸头。

    如果是遠(yuǎn)程調(diào)用,那對(duì)于這個(gè)asInterface方法來說舷蟀,參數(shù)IBinder其實(shí)是client的服務(wù)端代理恤磷,所以這里會(huì)返回一個(gè)Stub的內(nèi)部類Proxy對(duì)象,它的構(gòu)造方法里的IBinder就是服務(wù)端注冊(cè)給client的代理野宜。若現(xiàn)在我們調(diào)用getDemo02()方法扫步,其實(shí)調(diào)用的就是Proxy的getDemo02方法:

    image-20200712115602366

    取得了兩個(gè)Parcel對(duì)象,_data是用來保存Client傳遞過來的數(shù)據(jù)的匈子, _reply是用來保存返回給調(diào)用方(client)的數(shù)據(jù)的河胎。上面的是無參數(shù)的所以 _data沒有存儲(chǔ)額外數(shù)據(jù)的操作,看下面這段代碼:

    image-20200712124616268

    client傳遞的數(shù)據(jù)是demo虎敦,demo.writeToParcel(_data仿粹,0)保存到 _data搁吓。

    然后通過mRemote.transact方法(mRemote此時(shí)是代理Binder)調(diào)用服務(wù)端的真正對(duì)象的方法,這個(gè)方法后面做的工作牽扯到了Binder機(jī)制的深層邏輯吭历,這里先不討論堕仔,后面單獨(dú)整理,現(xiàn)在只要知道晌区,調(diào)用這個(gè)方法之后摩骨,最后會(huì)走到Stub的onTransact方法里(我們之前在Service里面定義的叫binder的Stub匿名類對(duì)象):

    img

    看到this.printList、this.getDemo02朗若、this.addDemo02了吧恼五,這調(diào)用的就是我們?cè)赟ervice里面定義的那個(gè)匿名類對(duì)象的對(duì)應(yīng)方法。對(duì)于getDemo02方法哭懈,因?yàn)闆]有傳遞數(shù)據(jù)進(jìn)來灾馒,所以沒有從data中取數(shù)據(jù)的操作,但是有返回值遣总,所以通過_result.writeToParcel(reply睬罗,android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE)方法把getDemo02的返回值寫到reply;而對(duì)于addDemo02方法旭斥,會(huì)通過Parcelable類型的CREATOR從之前mRemote的transact方法傳遞進(jìn)來的data構(gòu)造出當(dāng)時(shí)客戶端傳遞的真實(shí)數(shù)據(jù)容达,然后傳遞給真正的addDemo02()方法,因?yàn)闆]有返回值垂券,所以這里reply并沒有執(zhí)行操作花盐。

    中間還有部分代碼是Proxy和Stub的之間的數(shù)據(jù)傳遞的,包括判斷客戶端傳遞的非基本類型參數(shù)或引用型返回值是否為null菇爪,通過writeInt和readInt是否為0來判斷是否有必要將參數(shù)繼續(xù)往下傳遞算芯。

    值得注意的是:

    • out定向

      如果AIDL的方法參數(shù)是out定向,則服務(wù)端不會(huì)收到客戶端的數(shù)據(jù)凳宙,或者說它不會(huì)去取熙揍,Proxy中是這樣做的:
      image-20200712191124166

      可以看到_data并沒有繼續(xù)往下傳遞參數(shù),作為比較近速,如果是in或者inout定向時(shí):
      image-20200712191531343

      Stub的onTransact方法里是:

      image-20200712191918877

      可以看到,它是直接new了一個(gè)新對(duì)象堪旧,同樣削葱,作為對(duì)比,in和inout是這么做的:

      image-20200712192107399

      這里額外說明一下這個(gè)地方:

      image-20200712193927649

      這就是寫回給client傳遞過來的對(duì)象的值的地方淳梦,只有out和inout會(huì)有析砸,這就是前面說的為什么如果有out或者inout定向的話要給自定義Parcelable類手動(dòng)加上readFromParcel方法的原因。

    • in定向

      in定向修飾的話就是參數(shù)會(huì)使用客戶端傳遞過來的爆袍,Proxy中:

      image-20200712194523107

      Stub中:

      image-20200712194829706

      同時(shí)首繁,它不會(huì)將參數(shù)保存到reply中作郭,Proxy中也不會(huì)有dd.readFromParcel(reply)的操作。

    • inout定向

      inout沒什么特殊的弦疮,就是兼?zhèn)淞薸n和out夹攒。

總結(jié)

到此為止,我們順利地梳理了一遍AIDL從創(chuàng)建aidl文件到使用AIDL機(jī)制來實(shí)現(xiàn)跨進(jìn)程通信地流程胁塞,不過其中還有bindService方法之后Android框架做了什么咏尝、onTransact方法中return true之后Binder是如何去做的問號(hào),后面有時(shí)間會(huì)研究一下啸罢。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末编检,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子扰才,更是在濱河造成了極大的恐慌允懂,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件衩匣,死亡現(xiàn)場(chǎng)離奇詭異蕾总,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)舵揭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門谤专,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人午绳,你說我怎么就攤上這事置侍。” “怎么了拦焚?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵蜡坊,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我赎败,道長(zhǎng)秕衙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任僵刮,我火速辦了婚禮据忘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘搞糕。我一直安慰自己勇吊,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布窍仰。 她就那樣靜靜地躺著汉规,像睡著了一般。 火紅的嫁衣襯著肌膚如雪驹吮。 梳的紋絲不亂的頭發(fā)上针史,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天晶伦,我揣著相機(jī)與錄音,去河邊找鬼啄枕。 笑死婚陪,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的射亏。 我是一名探鬼主播近忙,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼智润!你這毒婦竟也來了及舍?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤窟绷,失蹤者是張志新(化名)和其女友劉穎锯玛,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體兼蜈,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡攘残,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了为狸。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歼郭。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖辐棒,靈堂內(nèi)的尸體忽然破棺而出病曾,到底是詐尸還是另有隱情,我是刑警寧澤漾根,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布泰涂,位于F島的核電站,受9級(jí)特大地震影響辐怕,放射性物質(zhì)發(fā)生泄漏逼蒙。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一寄疏、第九天 我趴在偏房一處隱蔽的房頂上張望是牢。 院中可真熱鬧,春花似錦陕截、人聲如沸驳棱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蹈胡。三九已至渊季,卻和暖如春朋蔫,著一層夾襖步出監(jiān)牢的瞬間罚渐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工驯妄, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留荷并,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓青扔,卻偏偏與公主長(zhǎng)得像源织,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子微猖,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348