AIDL使用詳解及原理

我們都知道荞雏,在Android中胎撤,系統(tǒng)會為每個進程分配對應的內(nèi)存空間伊脓,這部分內(nèi)存是彼此間相互獨立府寒,不可直接交互的,這樣的設(shè)計是處于安全性以及系統(tǒng)穩(wěn)定性方面考慮的,比如當我們的App奔潰時椰棘,不至于導致其他App無法運行纺棺,甚至死機等情況。那么邪狞,Android中是否就無法實現(xiàn)進程間通信呢祷蝌?答案當然是否定的。Android中進程通信的方式有很多帆卓,比如AIDL就可以實現(xiàn)這樣子的需求巨朦。


進程間通信.png

1.AIDL

? AIDL(Android Interface Define Language)是一種IPC通信方式,我們可以利用它來定義兩個進程相互通信的接口剑令。他是基于Service實現(xiàn)的一種線程間通信機制糊啡。它的本質(zhì)是C/S架構(gòu)的,需要一個服務器端吁津,一個客戶端棚蓄。

2.AIDL的使用

2.1創(chuàng)建aidl

? 首先我們在AndroidStudio中創(chuàng)建一個Andorid工程,

? 隨后添加一個module碍脏,作為aidl的服務端

? 在aidlserver中創(chuàng)建aild目錄梭依, 同時創(chuàng)建一個aidl文件

aidlserver目錄.png
// IMyAidlInterface.aidl
package com.yunzhou.aidlserver;

// Declare any non-default types here with import statements

interface IMyAidlInterface {

    /**
    * 自己添加的方法
    */
    int add(int value1, int value2);
}

? 這邊可以看到aidl的語法跟JAVA是一樣的,聲明了一個接口典尾,里面定義了aidl服務器端暴露給客戶端調(diào)用的方法役拴。

? 完成這部分操作之后還沒有結(jié)束,我們需要手動編譯程序钾埂,生成aidl對應的Java代碼

aidl生成過程.png
2.2實現(xiàn)接口河闰,并向客戶端放開接口
public class MyAidlService extends Service {
    public MyAidlService() {
    }

    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }

    private IBinder iBinder = new IMyAidlInterface.Stub(){
        @Override
        public int add(int value1, int value2) throws RemoteException {
            return value1 + value2;
        }
    };
}

? 我們創(chuàng)建了一個service,并在service內(nèi)部聲明了一個IBinder對象褥紫,它是一個匿名實現(xiàn)的IMyAidlInterface.Stub的實例(這部分我們后面講)姜性,同時我們在發(fā)現(xiàn)IMyAidlInterface.Stub實例實現(xiàn)了add方法,這個方法正是我們在aidl中聲明的供客戶端調(diào)用的方法髓考。

2.3客戶端調(diào)用aidl

? 首先在客戶端跟服務器一樣污抬,新建aidl目錄,將服務器端的aidl拷貝到客戶端绳军,這邊特別要注意印机,拷貝后的客戶端的aidl文件包目錄必須與服務器端保持一致,拷貝完后同樣時編譯工程门驾,讓客戶端也生成對應的java文件

客戶端使用aidl_目錄.png

? 其次就是在Activity的onCreate中綁定服務


 private ServiceConnection connection = new ServiceConnection() {
       @Override
       public void onServiceConnected(ComponentName name, IBinder service) {
           //綁定服務成功回調(diào)
           aidl = IMyAidlInterface.Stub.asInterface(service);
       }

       @Override
       public void onServiceDisconnected(ComponentName name) {
           //服務斷開時回調(diào)
           aidl = null;
       }
   };

@Override
protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      //do something
      bindService();
}

private void bindService(){
        Intent intent = new Intent();
        //Android 5.0開始射赛,啟動服務必須使用顯示的,不能用隱式的
        intent.setComponent(new ComponentName("com.yunzhou.aidlserver", "com.yunzhou.aidlserver.MyAidlService"));
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

? 綁定完服務奶是,就是進行調(diào)用了楣责,具體的頁面細節(jié)這邊不做展示竣灌,就是在Activity中放了一個按鈕,點擊按鈕進行遠程調(diào)用

int result = aidl.add(12, 12);
Log.e(TAG, "遠程回調(diào)結(jié)果:" + result);
遠程調(diào)用結(jié)果.png

? 可以看到秆麸,logcat打印出來結(jié)果初嘹,說明遠程調(diào)用成功了,至此aidl的整個流程就走完了沮趣。

3.AIDL可使用的參數(shù)類型

3.1基本數(shù)據(jù)類型

我們都知道Java有8中基本數(shù)據(jù)類型屯烦,分別為byte,char,short,int,long,float,double,boolean,那這8中數(shù)據(jù)類型是否都能作為aidl的參數(shù)進行傳遞呢?我們可以在aild中嘗試下房铭,并編譯驻龟,看看有沒有錯

void basicTypes(byte aByte, char aChar, short aShort, int anInt, long aLong, float aFloat,
            double aDouble, boolean aBoolean);

發(fā)現(xiàn)這樣子定義,無法成功編譯缸匪,經(jīng)過篩查發(fā)現(xiàn)aidl并不能支持short基本數(shù)據(jù)類型翁狐,至于為什么呢,可以看一看Android中的Parcel凌蔬,Parcel是不支持short的露懒,這應該是考慮到兼容性問題吧。

所以基本數(shù)據(jù)類型支持:byte,char,int,long,float,double,boolean

3.2引用數(shù)據(jù)類型

引用數(shù)據(jù)類型根據(jù)官方介紹砂心,可以使用String,CharSequence,List,Map懈词,當然,我們也可以使用自定義數(shù)據(jù)類型计贰。

3.3自定義數(shù)據(jù)類型

自定義數(shù)據(jù)類型钦睡,用于進程間通信的話蒂窒,必須實現(xiàn)Parcelable接口躁倒,Parcelable是類似于Java中的Serializable,Android中定義了Parcelable洒琢,用于進程間數(shù)據(jù)傳遞秧秉,對傳輸數(shù)據(jù)進行分解,編組的工作衰抑,相對于Serializable象迎,他對于進程間通信更加高效。

我們來看下下面的例子

public class User implements Parcelable {

    private int id;
    private String name;

    public User() {
    }

    public User(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public User(Parcel in){
      //注意順序G河弧@省!注意順序L吠M舫!注意順序S湓瘛=俾摇!
        this.id = in.readInt();
        this.name = in.readString();
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
      //注意順序!G肓铡稠通!注意順序!V掣尽刁笙!注意順序!@弧采盒!
        dest.writeInt(id);
        dest.writeString(name);
    }

    public static final  Parcelable.Creator<User> CREATOR = new Parcelable.Creator<User>(){

        @Override
        public User createFromParcel(Parcel source) {
            return new User(source);
        }

        @Override
        public User[] newArray(int size) {
            return new User[size];
        }
    };

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

? 這邊我們定義了一個User類,實現(xiàn)了Parcelable接口蔚润,大致的類結(jié)構(gòu)就是這個樣子的磅氨,需要注意的一點是,Parcelable對數(shù)據(jù)進行分解/編組的時候必須使用相同的順序嫡纠,字段以什么順序分解的烦租,編組時就以什么順序讀取數(shù)據(jù),不然會有問題除盏!

? 創(chuàng)建完實體后叉橱,我們需要創(chuàng)建一個aidl文件,來定義一下我們的User者蠕,否則User在aidl中無法識別

// IMyAidlInterface.aidl
package com.yunzhou.aidlserver;

parcelable User;

? 并在之前的服務器端aidl中新增方法

interface IMyAidlInterface {
    int add(int value1, int value2);
    List<User> addUser(in User user);
}

? 在service中實現(xiàn)新增的addUser方法

private ArrayList users;

@Override
public IBinder onBind(Intent intent) {
  users = new ArrayList<User>();
  return iBinder;
}

private IBinder iBinder = new IMyAidlInterface.Stub(){
  @Override
  public int add(int value1, int value2) throws RemoteException {
    return value1 + value2;
  }

  @Override
  public List<User> addUser(User user) throws RemoteException {
    users.add(user);
    return users;
  }
};

? 此時窃祝,server端的目錄就夠如下

parcelable_服務端結(jié)構(gòu).png

? 服務器端一切準備就緒后,我們對客戶端進行操作踱侣,首先粪小,我們將服務端的兩個aidl文件復制到客戶端,包結(jié)構(gòu)必須一致抡句,aidl文件發(fā)生變化不要忘記重新編譯代碼探膊。

? 然后,將User實體也復制到客戶端待榔,并且包結(jié)構(gòu)一致逞壁。

? 最后,在客戶端進行addUser的操作(這邊只是添加了一個按鈕锐锣,每點擊一次就調(diào)用一次addUser)

try {
  ArrayList<User> users = (ArrayList<User>) aidl.addUser(new User(12, "demaxiya"));
  Log.e(TAG, "遠程回調(diào)結(jié)果:" + users.toString());
} catch (RemoteException e) {
  e.printStackTrace();
}

? 此時客戶端的目錄結(jié)構(gòu)如下:


parcelable_客戶端結(jié)構(gòu).png

? 運行服務端與客戶端App腌闯,點擊addUser,輸出日下日志雕憔,說明調(diào)用成功


parcelable_遠程調(diào)用結(jié)果.png

4.AIDL原理

? 要了解aidl原理姿骏,我們需要看一下根據(jù)aidl生成的對應的java代碼了,

public interface IMyAidlInterface extends android.os.IInterface{
    public static abstract class Stub extends android.os.Binder implements com.yunzhou.aidlserver.IMyAidlInterface{...}
    public int add(int value1, int value2) throws android.os.RemoteException;
    public java.util.List<com.yunzhou.aidlserver.User> addUser(com.yunzhou.aidlserver.User user) throws android.os.RemoteException;
}

? 我們可以看到橘茉,生成的代碼結(jié)構(gòu)很簡單工腋,一個靜態(tài)抽象類Stub姨丈,以及aidl中定義的方法,其中Stub肯定時核心擅腰,我們深入閱讀

Stub.png

? Stub的目錄結(jié)構(gòu)也不復雜蟋恬,一個構(gòu)造函數(shù),一個asInterface方法趁冈,一個asBinder方法歼争,一個onTransact方法,一個Proxy代理類渗勘,這邊Proxy與Stub同時實現(xiàn)了我們定義的aidl沐绒,且Proxy中實現(xiàn)了我們在aidl中定義的add/addUser方法。下面兩個int為告訴系統(tǒng)的方法Id

? 在客戶端旺坠,我們綁定服務的時候通過Stub.asInterface()回去aidl對象乔遮,查看asInterface源碼,我們不難發(fā)現(xiàn)取刃,客戶端獲取到的其實時Stub.Proxy,一個遠程服務的代理蹋肮。

//客戶端獲取aidl
aidl = IMyAidlInterface.Stub.asInterface(service);

//Stub的asInterface
public static com.yunzhou.aidlserver.IMyAidlInterface asInterface(android.os.IBinder obj) {
  if ((obj==null)) {
    return null;
  }
  android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
  if (((iin!=null)&&(iin instanceof com.yunzhou.aidlserver.IMyAidlInterface))) {
    return ((com.yunzhou.aidlserver.IMyAidlInterface)iin);
  }
  return new com.yunzhou.aidlserver.IMyAidlInterface.Stub.Proxy(obj);
}

? 所以客戶端調(diào)用add/addUser方法其實調(diào)用的時Stub.Proxy中實現(xiàn)的add/addUser,在Stub.Proxy中我們可以看到一句核心代碼

//add
mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);

//addUser
mRemote.transact(Stub.TRANSACTION_addUser, _data, _reply, 0);

? 追溯源頭,mRemote其實就是IMyAidlInterface.Stub璧疗,隨意mRemote.transact傳遞到了IMyAidlInterface.Stub.OnTransact, onTransact中執(zhí)行了add/addUser,回調(diào)到了我們在服務端定義Service中聲明IBidner時重寫的add/addUser,這就是AIDL的整個流程坯辩。

private IBinder iBinder = new IMyAidlInterface.Stub(){
  @Override
  public int add(int value1, int value2) throws RemoteException {
    return value1 + value2;
  }

  @Override
  public List<User> addUser(User user) throws RemoteException {
    users.add(user);
    return users;
  }
};

? 下面用一張圖來總結(jié)aidl原理,這邊特別感謝imooc

aidl原理.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末崩侠,一起剝皮案震驚了整個濱河市漆魔,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌却音,老刑警劉巖改抡,帶你破解...
    沈念sama閱讀 216,744評論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異僧家,居然都是意外死亡雀摘,警方通過查閱死者的電腦和手機裸删,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,505評論 3 392
  • 文/潘曉璐 我一進店門八拱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涯塔,你說我怎么就攤上這事肌稻。” “怎么了匕荸?”我有些...
    開封第一講書人閱讀 163,105評論 0 353
  • 文/不壞的土叔 我叫張陵爹谭,是天一觀的道長。 經(jīng)常有香客問我榛搔,道長诺凡,這世上最難降的妖魔是什么东揣? 我笑而不...
    開封第一講書人閱讀 58,242評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮腹泌,結(jié)果婚禮上嘶卧,老公的妹妹穿的比我還像新娘。我一直安慰自己凉袱,他們只是感情好芥吟,可當我...
    茶點故事閱讀 67,269評論 6 389
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著专甩,像睡著了一般钟鸵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涤躲,一...
    開封第一講書人閱讀 51,215評論 1 299
  • 那天棺耍,我揣著相機與錄音,去河邊找鬼种樱。 笑死烈掠,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的缸托。 我是一名探鬼主播左敌,決...
    沈念sama閱讀 40,096評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼俐镐!你這毒婦竟也來了矫限?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,939評論 0 274
  • 序言:老撾萬榮一對情侶失蹤佩抹,失蹤者是張志新(化名)和其女友劉穎叼风,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體棍苹,經(jīng)...
    沈念sama閱讀 45,354評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡无宿,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,573評論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了枢里。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孽鸡。...
    茶點故事閱讀 39,745評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖栏豺,靈堂內(nèi)的尸體忽然破棺而出彬碱,到底是詐尸還是另有隱情,我是刑警寧澤奥洼,帶...
    沈念sama閱讀 35,448評論 5 344
  • 正文 年R本政府宣布巷疼,位于F島的核電站,受9級特大地震影響灵奖,放射性物質(zhì)發(fā)生泄漏嚼沿。R本人自食惡果不足惜估盘,卻給世界環(huán)境...
    茶點故事閱讀 41,048評論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望骡尽。 院中可真熱鬧忿檩,春花似錦、人聲如沸爆阶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,683評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辨图。三九已至班套,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間故河,已是汗流浹背吱韭。 一陣腳步聲響...
    開封第一講書人閱讀 32,838評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留鱼的,地道東北人理盆。 一個月前我還...
    沈念sama閱讀 47,776評論 2 369
  • 正文 我出身青樓,卻偏偏與公主長得像凑阶,于是被迫代替她去往敵國和親猿规。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,652評論 2 354

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

  • Jianwei's blog 首頁 分類 關(guān)于 歸檔 標簽 巧用Android多進程宙橱,微信姨俩,微博等主流App都在用...
    justCode_閱讀 5,915評論 1 23
  • Android跨進程通信IPC整體內(nèi)容如下 1、Android跨進程通信IPC之1——Linux基礎(chǔ)2师郑、Andro...
    隔壁老李頭閱讀 10,750評論 13 43
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理环葵,服務發(fā)現(xiàn),斷路器宝冕,智...
    卡卡羅2017閱讀 134,652評論 18 139
  • 本篇包含進程間通信——AIDL所涉及到的知識的自我總結(jié)(內(nèi)容詳細) 通過前段時間對AIDL的學習以及最近一些資料的...
    arvinljw閱讀 3,099評論 0 17
  • 三文魚去骨切塊地梨,平底鍋燒熱放入魚塊(不要放油)菊卷,中小火煎至兩面微黃取出。 四季豆湿刽、秋葵的烁、豆豉褐耳、青椒切碎诈闺,姜絲、蒜切...
    北美K哥閱讀 455評論 0 2