Android的進階學(xué)習(xí)(四)--AIDL的使用與理解

我最初接觸aidl的時候焙糟,就感覺這個好難,一下一大堆的代碼就出來了样屠〈┐椋總覺得這種東西會用就行,懂它什么的因該是大神們的事痪欲,但是知其然悦穿,不知其所以然,用起來總是覺得怪怪的业踢,所以就決定慢慢理一下它咧党。
其實一步一步看懂,也很簡單的陨亡。


aidl的使用

最常見的aidl的使用就是Service的跨進程通信了,那么我們就寫一個ActivityService的跨進程通信吧深员。
首先负蠕,我們就在AS里面新建一個aidl文件(ps:現(xiàn)在AS建aidl不要求和java包名相同了):

package aidl;
interface IMyInterface {
    String getInfor(String s);
}

可以看到,在這里面我們就一個方法getInfor(String s),接受一個字符串參數(shù)倦畅,然后返回一個字符串遮糖,恩,相當(dāng)?shù)暮唵巍?/p>

接著你sync project一下就可以在app/generated/source/aidl/debug/aidl里面發(fā)現(xiàn)由aidl文件生成的java文件了叠赐。點進去一看欲账,可能你也被這個貌似'龐大'的類給嚇住了,那現(xiàn)在我們就先不管它了芭概。

然后就看看Service:

public class MyService extends Service { 
   
public final static String TAG = "MyService";

private IBinder binder = new IMyInterface.Stub() {
        @Override       
        public String getInfor(String s) throws RemoteException { 
            Log.i(TAG, s); 
            return "我是 Service 返回的字符串"; 
       }
    };
@Overrid
public void onCreate() {
    super.onCreate(); 
    Log.i(TAG, "onCreat");    
}       
@Override    
public IBinder onBind(Intent intent) { 
    return binder;  
    }
}

這里我們寫了一個Service赛不,看一下也比較簡單。先new了一IMyInterface.Stub()并把它向上轉(zhuǎn)型成了IBinder罢洲,最后在onBind方法中返回回去踢故。可能你注意到了,在IMyInterface.Stub()的內(nèi)部我們重寫getInfor(String s) 方法殿较,沒錯這就是我們 aidl文件中定義的接口耸峭。
對了,因為我們希望看到的是跨進程通信淋纲,所以我們把MyService定義成新啟動一個進程:

<service
    android:name=".server.MyService"
    android:process="com.mathiasluo.remote" />

定義為啟動在新進程中劳闹,只需要在AndroidMainfest.xml中聲明是加上一個process屬性即可,不過這里有兩個地方值得注意:1.組件默認(rèn)的進程名就是包名;2.定義新的進程名的時候需要以包的形式(eg: com.luo.aidl)洽瞬。

接著本涕,我們繼續(xù)向下看:


public class MainActivity extends AppCompatActivity {
    public final static String TAG = "MainActivity";
    private IMyInterface myInterface;
    private ServiceConnection serviceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            myInterface = IMyInterface.Stub.asInterface(service);
            Log.i(TAG, "連接Service 成功");
            try {
                String s = myInterface.getInfor("我是Activity傳來的字符串");
                Log.i(TAG, "從Service得到的字符串:" + s);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e(TAG, "連接Service失敗");
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        startAndBindService();
    }
    private void startAndBindService() {
        Intent service = new Intent(MainActivity.this, MyService.class);
        //startService(service);
        bindService(service, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}

由于主要是ServiceActivity間的通信,所以為了讓代碼整潔就沒有寫UI
了片任。
onCreate(Bundle savedInstanceState)中偏友,我們調(diào)用了自己定義的一個方法startAndBindService(),這個方法里面我們生成了一個Intent对供,然后 bindService了這個Intent傳入了三個參數(shù)分別是Intent位他、ServiceConnectionFlag产场。

Intent我們就不用說了鹅髓,我們看看后面兩個參數(shù):
Activity中,我們new了一個ServiceConnection并實現(xiàn)了他的兩個方法onServiceConnected京景、onServiceDisconnected窿冯。在onServiceConnected中我們通過IMyInterface.Stub.asInterface(service)把傳來的IBinder轉(zhuǎn)換成了我們定義的IMyInterface。然后我們調(diào)用了getInfor方法确徙,傳遞了個字符串和獲取從MyService傳來的字符串醒串,并且打印了Log
對于我們傳的Context.BIND_AUTO_CREATE的意思就是說:如果綁定Service的時候鄙皇,Service還沒有被創(chuàng)建芜赌,就創(chuàng)建它。當(dāng)然伴逸,這里還有很多Flag缠沈,我也不一一說了。

然后错蝴,我們的編碼就完成了洲愤,開始運行測試一下把!

顷锰、

Paste_Image.png
Paste_Image.png

看一下運行結(jié)果柬赐,在這兩個不同的進程中都得到了我們想要的結(jié)果,所以官紫,一個用aidl實現(xiàn)的跨進程通信就這樣完成了躺率。當(dāng)然我們的demo(這次不能拼錯了)中玛界,getInfor沒有任何邏輯,你也可以加一些邏輯去執(zhí)行一些復(fù)雜的操作悼吱。


aidl的理解

現(xiàn)在我們會使用aidl了慎框,那我們還不去掀開它神秘的面紗。

Service中的IBinder

還記得我們在MyService中利用new IMyInterface.Stub()向上轉(zhuǎn)型成了IBinder然后在onBind方法中返回的后添。那我們就看看IMyInterface.Stub吧:

public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {
..........
}

可以看到笨枯,StubIMyInterface中的一個靜態(tài)抽象類,繼承了Binder遇西,并且實現(xiàn)了IMyInterface接口馅精。這也就解釋了我們定義IMyInterface.Stub的時候為什么需要實現(xiàn)IMyInterface中的方法了,也說明了為什么我們可以把IMyInterface.Stub向上轉(zhuǎn)型成IBinder了粱檀。

Activity中的IMyInterface

Activity中洲敢,通過ServiceConnection連接MyService并成功回調(diào)onServiceConnected中我們把傳回來的IBinder通過IMyInterface.Stub.asInterface(service)轉(zhuǎn)換成為IMyInterface,那就來看看這里是如何轉(zhuǎn)換的吧:

public static abstract class Stub extends android.os.Binder implements aidl.IMyInterface {

..........

public static aidl.IMyInterface asInterface(android.os.IBinder obj) {
    if ((obj == null)) {
        return null;
    }
    //檢查Binder是不是在當(dāng)前進程
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin != null) && (iin instanceof aidl.IMyInterface))) {
        return ((aidl.IMyInterface) iin);
    }
    return new aidl.IMyInterface.Stub.Proxy(obj);
}
}

首先茄蚯,我們因該明白的是压彭,傳回來的IBinder就是我們在ServiceonBind( )方法所returnIBinder,然后我們調(diào)用Stub中的靜態(tài)方法asInterface并把返回來的IBinder當(dāng)參數(shù)傳進去渗常。
asInterface方法中壮不,首先判斷了傳進來的IBinder是不是null,如果為null就返回一個null;接著就判斷傳進來的IBinder是不是就在當(dāng)前進程里面皱碘,如果是的話就直接返回IMyInterface询一,不是的話就返回IMyInterface.Stub.Proxy(obj)。這里我覺得需要明白的是:直接返回的IMyInterface是實現(xiàn)了定義的接口方法getInfor的癌椿。因為在IMyInterface.Stub中所實現(xiàn)的健蕊。當(dāng)然如果是在同一進程中,那么我們調(diào)用IMyInterface的方法時就是在本地調(diào)用方法踢俄,直接調(diào)用就可以了绊诲。

如果沒在同一進程,就會返回IMyInterface.Stub.Proxy(obj):

  private static class Proxy implements aidl.IMyInterface {
        private android.os.IBinder mRemote;
        Proxy(android.os.IBinder remote) {
            mRemote = remote;
        }
        @Override
        public android.os.IBinder asBinder() {
            return mRemote;
        }
        public java.lang.String getInterfaceDescriptor() {
            return DESCRIPTOR;
        }
        @Override
        public java.lang.String getInfor(java.lang.String s) throws android.os.RemoteException {
            android.os.Parcel _data = android.os.Parcel.obtain();
            android.os.Parcel _reply = android.os.Parcel.obtain();
            java.lang.String _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                _data.writeString(s);
               //傳送數(shù)據(jù)到遠(yuǎn)程的
                mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
                _reply.readException();
              //接受從遠(yuǎn)端傳回的數(shù)據(jù)
                _result = _reply.readString();
            } finally {
                _reply.recycle();
                _data.recycle();
            }
            return _result;
        } 
   }
    static final int TRANSACTION_getInfor = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
}

Proxy中褪贵,我們首先把Service連接成功返回的IBinder它的內(nèi)部變量mRemote,這里在提一下,這里得IBinder還是是MyServiceonBind所返回的抗俄。然后脆丁,當(dāng)我們調(diào)用IMyInterface的方法的時候,其實就是調(diào)用的Proxy的方法了动雹,這也是為什么這個類叫做Porxy的原因了槽卫。

當(dāng)調(diào)用IMyInterface.getInfor(String s) ,我們就看Proxy中的getInfor,先獲取了兩個Parcel對象 _data胰蝠、_data歼培,從變量名就可以看出震蒋,一個是傳送數(shù)據(jù)的,另一個則是接受返回數(shù)據(jù)的躲庄。接著查剖,向_data中寫入了DESCRIPTOR(也就是這個類的全名),再寫入了方法參數(shù)噪窘。然后就到了最重要的一步了笋庄,

mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);

這里我們調(diào)用了IBindertransact方法,來把數(shù)據(jù)傳給遠(yuǎn)端的服務(wù)器倔监。然后在我們遠(yuǎn)程的MyService中直砂,里面的Stub中就會回調(diào)onTransact()(因為你把數(shù)據(jù)傳個遠(yuǎn)程的服務(wù),遠(yuǎn)端的服務(wù)收到數(shù)據(jù)也就回調(diào)了)

注意:這里是在遠(yuǎn)程的服務(wù)里調(diào)用的浩习。

@Overridepublic boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
    switch (code) {
        case INTERFACE_TRANSACTION: {
            reply.writeString(DESCRIPTOR);
            return true;
        }
        case TRANSACTION_getInfor: {
            data.enforceInterface(DESCRIPTOR);
            java.lang.String _arg0;
           //取出參數(shù)
            _arg0 = data.readString();
           // 遠(yuǎn)程服務(wù)調(diào)用自己本地實現(xiàn)的方法獲取返回值
            java.lang.String _result = this.getInfor(_arg0);
            reply.writeNoException();
           //寫入返回值
            reply.writeString(_result);
            return true;
        }
    }
    return super.onTransact(code, data, reply, flags);
}

onTransact方法是在Stub的內(nèi)部實現(xiàn)的静暂。
先看一下它的四個參數(shù):
code:每個方法都有一個int類型的數(shù)字用來區(qū)分(后面中的swicth),在我們例子中也就是我們Proxy中的Stub.TRANSACTION_getInfor谱秽。
data:傳過來的數(shù)據(jù)洽蛀,其中包含我們的參數(shù),以及類的描述弯院。
reply:傳回的數(shù)據(jù)辱士,我們要寫入是否發(fā)生了Exception,以及返回值
flags:該方法是否有返回值 ,0表示有返回值听绳。

調(diào)用onTransact就表示有數(shù)據(jù)傳來颂碘,首先就會通過swicth判斷是哪個方法,然后取出方法參數(shù)椅挣,調(diào)用本地實現(xiàn)的方法獲取返回值头岔,寫入返回值到reply。最后鼠证,返回true峡竣,才會把數(shù)據(jù)發(fā)送出去诬垂,發(fā)揮false就不會把結(jié)果返回給Activity了由捎。這里也就是說,只有返回true,我們Proxy中才能接受從遠(yuǎn)端傳回的數(shù)據(jù)塑煎。

               //傳送數(shù)據(jù)到遠(yuǎn)程的
                mRemote.transact(Stub.TRANSACTION_getInfor, _data, _reply, 0);
                _reply.readException();
               //接受從遠(yuǎn)端傳回的數(shù)據(jù)
                _result = _reply.readString();

注意:Service也是把數(shù)據(jù)發(fā)送出來荠列,讓客戶端接受的类浪。

Service發(fā)出了數(shù)據(jù),客戶端接收到了肌似,就會一層一層返回去费就。所以,當(dāng)我們簡單的調(diào)用IMyInterfacegetInfor時候川队,先是Proxytransact發(fā)送出數(shù)據(jù)力细,然后服務(wù)端的onTransact接受并處理傳來的數(shù)據(jù)睬澡,再把處理得到的數(shù)據(jù)寫入返回值并發(fā)送給客戶端,客戶端讀取值后就成為調(diào)用方法的返回值返回了眠蚂。

然后aidl的基本原理就是這樣了煞聪,看明白了aidl,才發(fā)現(xiàn)原來aidl不過就是幫我們生成了那些數(shù)據(jù)寫入河狐,傳送米绕,讀取的方法而已。所以馋艺,我們自己寫一個不要aidl的跨進程通信也是很容易的栅干。


最后

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市捐祠,隨后出現(xiàn)的幾起案子碱鳞,更是在濱河造成了極大的恐慌,老刑警劉巖踱蛀,帶你破解...
    沈念sama閱讀 218,858評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件窿给,死亡現(xiàn)場離奇詭異,居然都是意外死亡率拒,警方通過查閱死者的電腦和手機崩泡,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來猬膨,“玉大人角撞,你說我怎么就攤上這事〔眨” “怎么了谒所?”我有些...
    開封第一講書人閱讀 165,282評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長沛申。 經(jīng)常有香客問我劣领,道長,這世上最難降的妖魔是什么铁材? 我笑而不...
    開封第一講書人閱讀 58,842評論 1 295
  • 正文 為了忘掉前任尖淘,我火速辦了婚禮,結(jié)果婚禮上著觉,老公的妹妹穿的比我還像新娘村生。我一直安慰自己,他們只是感情好固惯,可當(dāng)我...
    茶點故事閱讀 67,857評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著缴守,像睡著了一般葬毫。 火紅的嫁衣襯著肌膚如雪镇辉。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,679評論 1 305
  • 那天贴捡,我揣著相機與錄音忽肛,去河邊找鬼。 笑死烂斋,一個胖子當(dāng)著我的面吹牛屹逛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播汛骂,決...
    沈念sama閱讀 40,406評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼罕模,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了帘瞭?” 一聲冷哼從身側(cè)響起淑掌,我...
    開封第一講書人閱讀 39,311評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蝶念,沒想到半個月后抛腕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,767評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡媒殉,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,945評論 3 336
  • 正文 我和宋清朗相戀三年担敌,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片廷蓉。...
    茶點故事閱讀 40,090評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡全封,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出苦酱,到底是詐尸還是另有隱情售貌,我是刑警寧澤,帶...
    沈念sama閱讀 35,785評論 5 346
  • 正文 年R本政府宣布疫萤,位于F島的核電站颂跨,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏扯饶。R本人自食惡果不足惜恒削,卻給世界環(huán)境...
    茶點故事閱讀 41,420評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望尾序。 院中可真熱鬧钓丰,春花似錦、人聲如沸每币。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至梦鉴,卻和暖如春李茫,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背肥橙。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評論 1 271
  • 我被黑心中介騙來泰國打工魄宏, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人存筏。 一個月前我還...
    沈念sama閱讀 48,298評論 3 372
  • 正文 我出身青樓宠互,卻偏偏與公主長得像,于是被迫代替她去往敵國和親椭坚。 傳聞我的和親對象是個殘疾皇子予跌,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,033評論 2 355

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

  • Jianwei's blog 首頁 分類 關(guān)于 歸檔 標(biāo)簽 巧用Android多進程,微信藕溅,微博等主流App都在用...
    justCode_閱讀 5,917評論 1 23
  • Android跨進程通信IPC整體內(nèi)容如下 1匕得、Android跨進程通信IPC之1——Linux基礎(chǔ)2、Andro...
    隔壁老李頭閱讀 10,762評論 13 43
  • 上篇文章介紹了IPC機制的基本概念以及簡單使用巾表,文章鏈接:Android 關(guān)于IPC機制的理解(一) 這篇文章主要...
    老實任閱讀 726評論 0 2
  • 采用的是普通篩選法汁掠,建數(shù)組標(biāo)記,值為1時表示素數(shù)集币,在打印的時候先低位進棧再出棧輸出運行效果是這樣的 不知道怎么調(diào)緩...
    puzzledsky閱讀 434評論 0 0
  • 冬夜里考阱, 我在披星戴月, 堅持獨自漂泊鞠苟, 卻寒風(fēng)凜冽乞榨, 凍僵了腳步。 冬夜里当娱, 我在奮不顧身吃既, 堅持走遍夜景, 卻...
    多果加閱讀 203評論 0 0