談?wù)剬?duì)AIDL的理解

最近一些在準(zhǔn)備各種校招面試匣椰,在安卓面試的時(shí)候,總會(huì)被問到AIDL,跨進(jìn)程通信的知識(shí)绩脆,由于平時(shí)基本沒有AIDL跨進(jìn)程通信的需求(一般用廣播和contentprovider),所以AIDL這一塊一直很不熟悉橄抹,于是馬上去查閱了相關(guān)的資料靴迫,引發(fā)了一些思考。

跨進(jìn)程

為什么要有跨進(jìn)程通信這個(gè)概念呢楼誓?我們學(xué)習(xí)操作系統(tǒng)時(shí)都知道玉锌,進(jìn)程是系統(tǒng)分配資源的基本單位(雖然不太嚴(yán)謹(jǐn)),每個(gè)進(jìn)程都有自己獨(dú)立的空間地址疟羹,這個(gè)地址是邏輯地址主守,同一個(gè)進(jìn)程內(nèi)的線程共用這些資源,所以我們平時(shí)編程的時(shí)候用多線程去訪問同一個(gè)對(duì)象榄融,是沒有問題的参淫,從java的角度來說就是它們?cè)L問到的是同一個(gè)堆。但是多進(jìn)程就不一樣了愧杯,一個(gè)進(jìn)程里面的對(duì)象是不能直接被另一個(gè)進(jìn)程訪問到的涎才,多進(jìn)程應(yīng)用要正常工作,必需要有一個(gè)方式民效,來實(shí)現(xiàn)進(jìn)程之間的溝通憔维。

通信與Binder

什么是通信涛救,就是信息的傳遞或流動(dòng),在計(jì)算機(jī)的世界里业扒,所有的信息都是二進(jìn)制流检吆,如果你實(shí)現(xiàn)了將一些字節(jié)從一個(gè)進(jìn)程發(fā)送給另一個(gè)進(jìn)程,那就是實(shí)現(xiàn)了跨進(jìn)程通信程储,很明顯Socket就是一種跨進(jìn)程通信的方式蹭沛,只不過Socket從發(fā)送到接收,期間經(jīng)過了多次數(shù)據(jù)的拷貝章鲤,效率低摊灭。Linux上還有其他的跨進(jìn)程通信方式,它們各有優(yōu)缺點(diǎn)败徊,但谷歌在Android上提出了一個(gè)新的跨進(jìn)程通信的方案Binder帚呼。什么是Binder呢?要把這個(gè)東西說清楚皱蹦,涉及到的內(nèi)容太多太深了煤杀,想要深入學(xué)習(xí)的推薦一個(gè)博客Binder系列—開篇,里面有圖有文共花了十篇文章從源碼的角度分析了一遍,總得來說沪哺,它就是一種通過內(nèi)存拷貝的方式實(shí)現(xiàn)了從一個(gè)進(jìn)程向另一個(gè)進(jìn)程發(fā)送字節(jié)的技術(shù)(好象說了等于沒說)

AIDL

常說AIDL是安卓的一種跨進(jìn)程通信的方式沈自,其實(shí)不太嚴(yán)謹(jǐn),應(yīng)該說Binder才是一種跨進(jìn)程通信的方式辜妓,而AIDL是Binder的一種具體應(yīng)用枯途,有點(diǎn)類似于網(wǎng)絡(luò)中的傳輸層和應(yīng)用層,IBinder有自己的通信協(xié)議籍滴,負(fù)責(zé)建立和維護(hù)連接酪夷,發(fā)送數(shù)據(jù),而AIDL則定義了更上一層的傳輸內(nèi)容异逐,而像ContentProvider捶索、廣播同樣是利用IBinder進(jìn)行工作的。

那么AIDL到底是做什么的呢灰瞻?

用一句話來總結(jié)就是--接口的跨進(jìn)程調(diào)用腥例。舉個(gè)栗子,進(jìn)程A調(diào)用 Book getBook(int id)方法酝润,另一個(gè)進(jìn)程B響應(yīng)這個(gè)方法并返回內(nèi)容Book燎竖,A拿到從B過來的這個(gè)Book,如果愿意要销,不只A可以通過getBook獲取Book對(duì)象构回,其他進(jìn)程CDE同樣可以調(diào)用這個(gè)方法來獲取來自B的對(duì)象,典型的C/S模式。

實(shí)現(xiàn)之前

先看在這過程中兩者之間傳遞了哪些信息纤掸。首先A要讓B知道脐供,你調(diào)用的是getBook這個(gè)方法,而不是getName或者其它方法,其次方法的參數(shù)id也要傳遞給B借跪。而對(duì)B來說政己,要把找到的book對(duì)象傳遞給A。這就引出了一個(gè)問題掏愁,對(duì)象和方法是一個(gè)編程的概念歇由,我們只可以傳字節(jié),那怎么用二進(jìn)制數(shù)據(jù)來表達(dá)對(duì)象和方法呢果港?

  1. 二進(jìn)制與對(duì)象
    對(duì)象與二進(jìn)制數(shù)據(jù)的轉(zhuǎn)換沦泌,也就是我們常說的對(duì)象的序列化和反序列化,安卓中的具體的方案就是Parcelable接口辛掠。Parcelable的具體思想是谢谦,它不關(guān)心你的對(duì)象有哪些成員,而是給你提供了一個(gè)Parcel對(duì)象公浪,你可以把Parcel對(duì)象當(dāng)作一個(gè)數(shù)據(jù)包一樣他宛,當(dāng)你要序列化的時(shí)候,往Parcel里面寫對(duì)象的成員值欠气,具體寫哪些成員、成員的順序等自己決定镜撩,因?yàn)榈綍r(shí)候從Parcel讀數(shù)據(jù)也是你自己來讀预柒,Parcel不關(guān)心你的數(shù)據(jù)結(jié)構(gòu)。而另一端袁梗,也就是需要反序列化的一端宜鸯,需要你根據(jù)之前寫的順序去讀Parcel里的值并利用這些值去構(gòu)造你的對(duì)象。下面是一個(gè)實(shí)現(xiàn)了Parcelable的Book類
public class Book implements Parcelable {
    public int id;
    public String name;
    protected Book(Parcel in)
    {
        //在構(gòu)造的時(shí)候遮怜,從Parcel里面讀成員值淋袖,順序和寫的時(shí)候一樣
        id = in.readInt();
        name = in.readString();
    }
    public static final Creator<Book> CREATOR = new Creator<Book>() {
        @Override
        public Book createFromParcel(Parcel in)
        {
            return new Book(in);
        }
        @Override
        public Book[] newArray(int size)
        {
            return new Book[size];
        }
    };
    @Override
    public int describeContents()
    {
        return 0;
    }
    @Override
    public void writeToParcel(Parcel dest, int flags)
    {
        //在序列化的時(shí)候被調(diào)用,往Parcel里面寫數(shù)據(jù)
        dest.writeInt(id);
        dest.writeString(name);
    }
}
  1. 表示方法
    方法的二進(jìn)制表達(dá)就更簡(jiǎn)單了锯梁,方法說白了就是一個(gè)符號(hào)即碗,那就可以直接用數(shù)字0、1陌凳、2...去代表每個(gè)方法剥懒,而數(shù)據(jù)的含義由我們自己來決定。

用Binder通信

IBinder是一個(gè)接口(這里指的是java層的IBinder)合敦,當(dāng)我們綁定另一個(gè)進(jìn)程的Service的時(shí)候初橘,會(huì)在onBind方法里面返回一個(gè)IBinder對(duì)象,而客戶端也會(huì)在onServiceConnected拿到一個(gè)代表服務(wù)的IBinder對(duì)象,但這兩個(gè)對(duì)象不是同一個(gè)對(duì)象來的保檐,畢竟來自兩個(gè)進(jìn)程耕蝉,IBinder有一個(gè)核心方法,注意方法的參數(shù)

public boolean transact(int code, Parcel data, Parcel reply, int flags)  
// code 用來代表方法
// data 可以裝方法的參數(shù)
// reply 可以裝方法的返回值 

進(jìn)程之間就是通過調(diào)用IBinder的這個(gè)方法來進(jìn)行通信的夜只,雖然兩個(gè)IBinder不是同一個(gè)對(duì)象赔硫,但ServiceManger負(fù)責(zé)將它們關(guān)聯(lián)起來,當(dāng)我們本地調(diào)用transact方法時(shí)盐肃,遠(yuǎn)程的IBinder的transact就會(huì)被調(diào)用爪膊,其中間經(jīng)過了ServiceManger,顯然這也是一次跨進(jìn)程調(diào)用砸王,這樣一來就好像為了實(shí)現(xiàn)跨進(jìn)程調(diào)用推盛,就要先實(shí)現(xiàn)跨進(jìn)程調(diào)用,這就要涉及到最初的ServiceManger是怎么跑起來的谦铃,等以后把IBinder的native層吃透了再寫一篇文章來講了耘成。
現(xiàn)在,兩個(gè)進(jìn)程可以通過transact方法來通信了驹闰,可是怎么調(diào)用getBook呢瘪菌?現(xiàn)在來實(shí)現(xiàn)Book getBook(int id)的整個(gè)過程:調(diào)用Ibinder的transact方法,用一個(gè)數(shù)字(比如2)代表getBook嘹朗,即參數(shù)code為2师妙,把id寫進(jìn)data里面,服務(wù)端IBinder響應(yīng)transact方法屹培,通過code知道調(diào)用的是getBook默穴,從data中取出id并找到對(duì)應(yīng)的Book,然后將Book寫進(jìn)reply里面褪秀,客戶端再?gòu)膔eply里面讀出返回值蓄诽,再將返回值構(gòu)造成Book對(duì)象。整個(gè)getBook過程就完成了媒吗。
可是這樣也太麻煩了吧仑氛,調(diào)用一個(gè)接口就這么麻煩,如果接口很多豈不是更麻煩闸英,而且怎么保證通信的規(guī)范,比如怎么返回Null值自阱,怎么返回異常?

AIDL文件定義接口

剛才說到沛豌,調(diào)用一個(gè)接口太麻煩了赃额,而且每個(gè)調(diào)用邏輯都是差不多的,那么有沒有辦法可以少寫代碼跳芳,我只要直接調(diào)用getBook()就可以幫我做完所有工作呢?那就是用AIDL文件來定義接口飞盆。當(dāng)我們用AIDL文件來描述我們的接口時(shí),SDK就會(huì)自動(dòng)幫我們生成相應(yīng)的Java代碼吓歇,主要內(nèi)容就是實(shí)現(xiàn)我剛才說的繁瑣流程。所以說城看,AIDL文件并不是什么高級(jí)的東西,只是用來描述我們的接口测柠,然后sdk會(huì)根據(jù)AIDL文件來生成Java代碼,如果你不用AIDL文件缘滥,直接手寫代碼也是可以的。下面看一個(gè)簡(jiǎn)單的例子

//book.aidl
package com.xxx.aidlsample.aidl;
parcelable Book;

// BookManagerInterface.aidl
package com.xxx.aidlsample.aidl;
import com.xxx.aidlsample.aidl.Book;
interface BookManagerInterface {
   Book getBook(int id);
}

定義兩個(gè)aidl文件并編譯后朝扼,自動(dòng)生成了一個(gè)BookManagerInterface類,這個(gè)類代碼十分好理解吟税,下面只說關(guān)鍵的幾個(gè)地方凹耙。
首先,它為getBook方法生成了一個(gè)靜態(tài)常量肠仪,也就是TRANSACTION_getBook代表getBook這個(gè)方法,用作transact的code參數(shù)

static final int TRANSACTION_getBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);

生成一個(gè)描述字符串备典,作為IBinder的代表

private static final java.lang.String DESCRIPTOR = "com.xxx.aidlsample.aidl.BookManagerInterface";

BookManagerInterface有一個(gè)sub的內(nèi)部抽象類(抽象方法為getBook)异旧,作為服務(wù)端的IBinder實(shí)現(xiàn),我們?cè)趕ervice的onBind中返回的就是sub的實(shí)現(xiàn)類提佣∷庇迹看它的transact方法實(shí)現(xiàn)


        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
        {
            switch (code) //匹配方法
            {
                ...
                case TRANSACTION_getBook: //如果是getBook
                {
                    data.enforceInterface(DESCRIPTOR);//驗(yàn)證描述符,確卑杵粒客戶端和服務(wù)端是對(duì)應(yīng)的
                    int _arg0;
                    _arg0 = data.readInt();//讀方法參數(shù)潮针,book id
                    com.dzy.aidlsample.aidl.Book _result = this.getBook(_arg0);//調(diào)用真正的getBook邏輯,這里的getBook是個(gè)抽象方法
                    reply.writeNoException();//表示沒有異常
                    if ((_result != null))
                    {
                        reply.writeInt(1);//表示結(jié)果不為null
                        //將找到的Book寫入reply
                        _result.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
                    } else
                    {
                        reply.writeInt(0);
                    }
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

再看客戶端的代理類倚喂,同樣是一個(gè)內(nèi)部類每篷,當(dāng)我們調(diào)用代理類的getBook

public com.dzy.aidlsample.aidl.Book getBook(int id) throws android.os.RemoteException
            {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                com.dzy.aidlsample.aidl.Book _result;
                try
                {
                    _data.writeInterfaceToken(DESCRIPTOR);//寫入描述符
                    _data.writeInt(id); //寫入?yún)?shù) id
                     //調(diào)用transact瓣戚,code就是getBook的指示常量
                    mRemote.transact(Stub.TRANSACTION_getBook, _data, _reply, 0);
                    _reply.readException();
                    if ((0 != _reply.readInt())) //如果返回結(jié)果不為null
                    {
                        //通過reply構(gòu)造返回的對(duì)象
                        _result = com.dzy.aidlsample.aidl.Book.CREATOR.createFromParcel(_reply);
                    } else
                    {
                        _result = null;
                    }
                } finally
                {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }

可以看到data和reply參數(shù),客戶端這邊是怎么寫入數(shù)據(jù)的焦读,服務(wù)端就怎么讀出來子库,一一對(duì)應(yīng),就像我們的網(wǎng)絡(luò)協(xié)議一樣矗晃。

最后總結(jié)一下仑嗅,AIDL的核心是Binder,我們通過AIDL文件來描述接口张症,使得到一個(gè)封裝好的IBinder代理仓技,來實(shí)現(xiàn)接口的遠(yuǎn)程調(diào)用。Binder是Android里面一個(gè)很重要的概念俗他,是Android各種ManagerService比如AMS脖捻、PMS的工作基礎(chǔ),如果要深入理解Android系統(tǒng)拯辙,就不得不理解Binder的原理郭变。本文跳過了很多核心的內(nèi)容,只講JAVA層的一些原理涯保。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诉濒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子夕春,更是在濱河造成了極大的恐慌未荒,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件及志,死亡現(xiàn)場(chǎng)離奇詭異片排,居然都是意外死亡速侈,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門冶共,熙熙樓的掌柜王于貴愁眉苦臉地迎上來捅僵,“玉大人庙楚,你說我怎么就攤上這事趴樱。” “怎么了窜司?”我有些...
    開封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)金刁。 經(jīng)常有香客問我尤蛮,道長(zhǎng)斯议,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任坯临,我火速辦了婚禮看靠,結(jié)果婚禮上液肌,老公的妹妹穿的比我還像新娘。我一直安慰自己谤祖,他們只是感情好粥喜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開白布橘券。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪鬓梅。 梳的紋絲不亂的頭發(fā)上谨湘,一...
    開封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音坊罢,去河邊找鬼。 笑死物遇,一個(gè)胖子當(dāng)著我的面吹牛询兴,可吹牛的內(nèi)容都是我干的起趾。 我是一名探鬼主播,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼眶根,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼属百!你這毒婦竟也來了艺骂?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎忧额,沒想到半個(gè)月后睦番,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡巩检,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年兢哭,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了迟螺。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锉桑,死狀恐怖民轴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情杉武,我是刑警寧澤,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站旦部,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏容燕。R本人自食惡果不足惜婚度,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一醋虏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧颈嚼,春花似錦阻课、人聲如沸艰匙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至骤公,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間凌节,已是汗流浹背洒试。 一陣腳步聲響...
    開封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工卒煞, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留叼架,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓扮饶,卻偏偏與公主長(zhǎng)得像甜无,于是被迫代替她去往敵國(guó)和親哥遮。 傳聞我的和親對(duì)象是個(gè)殘疾皇子昔善,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容