AIDL使用

1泻帮、定義

AIDL(Android Interface Define Language) 是IPC進(jìn)程間通信方式的一種.用于生成可以在Android設(shè)備上兩個(gè)進(jìn)程之間進(jìn)行進(jìn)程間通信(interprocess communication, IPC)的代碼.

2精置、使用

2.1)實(shí)例說明

假設(shè)一個(gè)情景我們需要計(jì)算a+b,我們需要在客戶端傳遞兩個(gè)參數(shù)a和b锣杂,然后將參數(shù)傳遞給服務(wù)端(另一個(gè)進(jìn)程)來進(jìn)行計(jì)算脂倦,計(jì)算結(jié)果傳遞給客戶端。

2.2)新建一個(gè)項(xiàng)目作為服務(wù)端蹲堂,在項(xiàng)目中創(chuàng)建AIDL文件

命名為IImoocAIDL.aidl狼讨,點(diǎn)擊同步sycn project,查看build內(nèi)是否自動(dòng)生成IImoocAIDL文件

// IImoocAIDL.aidl
package com.mecury.aidltest;
// Declare any non-default types here with import statements
interface IImoocAIDL {    
    //計(jì)算num1 + num2    
    int add(int num1,int num2);
}

2.3)自動(dòng)生成AIDL文件

根據(jù)自動(dòng)生成的AIDL文件分析AIDL通信原理:
文件有一個(gè)叫做proxy的類柒竞,這是一個(gè)代理類政供,這個(gè)類運(yùn)行在客戶端中,其實(shí)AIDL實(shí)現(xiàn)的進(jìn)程間的通信并不是直接的通信朽基,客戶端和服務(wù)端都是通過proxy來進(jìn)行通信的:客戶端調(diào)用的方法實(shí)際是調(diào)用是proxy中的方法布隔,然后proxy通過和服務(wù)端通信將返回的結(jié)果返回給客戶端。

/*
 * This file is auto-generated.  DO NOT MODIFY.
 * Original file: F:\\AS for android\\AIDLTest\\aidlclient\\src\\main\\aidl\\com\\mecury\\aidltest\\IImoocAIDL.aidl
 */
package com.mecury.aidltest;
// Declare any non-default types here with import statements

public interface IImoocAIDL extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements com.mecury.aidltest.IImoocAIDL {
        private static final java.lang.String DESCRIPTOR = "com.mecury.aidltest.IImoocAIDL"; //Binder的唯一標(biāo)識(shí)

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an com.mecury.aidltest.IImoocAIDL interface,
         * generating a proxy if needed.
         */
        //將服務(wù)端的Binder對(duì)象轉(zhuǎn)換成客戶需要的AIDL對(duì)象稼虎,轉(zhuǎn)換區(qū)分進(jìn)程衅檀,客戶端服務(wù)端位于同一進(jìn)程,返回服務(wù)端的
        //Stub對(duì)象本身霎俩;否則返回的是系統(tǒng)的封裝后的Stub.proxy對(duì)象哀军。
        public static com.mecury.aidltest.IImoocAIDL asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof com.mecury.aidltest.IImoocAIDL))) {
                return ((com.mecury.aidltest.IImoocAIDL) iin);
            }
            return new com.mecury.aidltest.IImoocAIDL.Stub.Proxy(obj);
        }
        //返回當(dāng)前Binder對(duì)象
        @Override
        public android.os.IBinder asBinder() {
            return this;
        }

        //運(yùn)行在服務(wù)端的Binder線程池中,接受到proxy的值并進(jìn)行相應(yīng)的處理
        @Override
        public 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_add: {
                    data.enforceInterface(DESCRIPTOR);
                    //讀取客戶端傳遞過來再data中存儲(chǔ)的方法的參數(shù)
                    int _arg0;
                    _arg0 = data.readInt();
                    int _arg1;
                    _arg1 = data.readInt();
                    //調(diào)用方法
                    int _result = this.add(_arg0, _arg1);
                    //將計(jì)算結(jié)果寫入reply中打却,如果是void杉适,就不需要返回值,如果是需要返回值柳击,則會(huì)調(diào)用reply.writeInt(_result);
                    reply.writeNoException();
                    reply.writeInt(_result);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags); //向Transact傳遞數(shù)據(jù)
        }

        //代理類猿推,運(yùn)行在客戶端
        private static class Proxy implements com.mecury.aidltest.IImoocAIDL {
            private android.os.IBinder mRemote; //聲明一個(gè)IBinder對(duì)象

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }
            //返回當(dāng)前Binder對(duì)象
            @Override
            public android.os.IBinder asBinder() {
                return mRemote;
            } 

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            //客戶端調(diào)用此方法,傳遞進(jìn)來num1和num2兩個(gè)參數(shù)捌肴,
            @Override
            public int add(int num1, int num2) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                android.os.Parcel _reply = android.os.Parcel.obtain();
                int _result;
                try {
                    //向_data中寫入?yún)?shù)
                    _data.writeInterfaceToken(DESCRIPTOR);
                    _data.writeInt(num1);
                    _data.writeInt(num2);
                    //通過transact方法向服務(wù)端傳遞參數(shù)蹬叭,并調(diào)用了方法藕咏,返回的結(jié)果寫入_reply中
                    //第一個(gè)參數(shù)為唯一碼,從000001開始秽五,有幾個(gè)方法就有幾個(gè)孽查,兩邊app的aidl方法順序不能亂
                    mRemote.transact(Stub.TRANSACTION_add, _data, _reply, 0);
                    _reply.readException();
                    _result = _reply.readInt();
                } finally {
                    _reply.recycle();
                    _data.recycle();
                }
                return _result;
            }
        }
        //標(biāo)識(shí)位
        static final int TRANSACTION_add = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    //計(jì)算num1 + num2
    public int add(int num1, int num2) throws android.os.RemoteException;
}

代碼中方法說明:
(1)DESCRIPTION
Binderd的唯一標(biāo)識(shí),一般用當(dāng)前的類名表示筝蚕。
asInterface(android.os.IBinder obj)
用于將服務(wù)端的Binder對(duì)象轉(zhuǎn)換為客戶端需要的AIDL接口類型的對(duì)象卦碾,轉(zhuǎn)換區(qū)分進(jìn)程,客戶端服務(wù)端位于同一進(jìn)程起宽,返回服務(wù)端的 //Stub對(duì)象本身;否則返回的是系統(tǒng)的封裝后的Stub.proxy對(duì)象济榨。
(2)asBInder
返回Binder對(duì)象
(3)onTransact
此方法運(yùn)行在服務(wù)端中的Binder線程池中坯沪,當(dāng)客戶端發(fā)起跨進(jìn)程請(qǐng)求時(shí),遠(yuǎn)程請(qǐng)求會(huì)通過系統(tǒng)底層封裝后交由此方法處理擒滑。
(4)Proxy#add
此方法運(yùn)行在客戶端腐晾,當(dāng)客戶端遠(yuǎn)程調(diào)用此方法時(shí),它的內(nèi)部實(shí)現(xiàn)是這樣的:首先創(chuàng)建該方法所需要的輸入型Parcel對(duì)象_data丐一、輸出型Parcel對(duì)象_reple和返回值對(duì)象_result,然后將該方法的參數(shù)信息寫入_data中藻糖;接著調(diào)用transact方法來發(fā)RPC請(qǐng)求,同時(shí)當(dāng)前線程掛起库车;然后服務(wù)端的onTransact方法會(huì)被調(diào)用巨柒,直到RPC過程返回后,當(dāng)前線程繼續(xù)執(zhí)行柠衍,并從_reply中取出RPC過程返回的結(jié)果洋满,寫入_result中。

2.4)新建客戶端

File-》new–》new module–》phone & table module珍坊。命名為aidlclient.java牺勾。同樣要在客戶端創(chuàng)建AIDL文件,里面的包名和所在位置要求完全一樣阵漏。

2.5)在服務(wù)端創(chuàng)建一個(gè)Service用來監(jiān)聽客戶端連接請(qǐng)求

public class IRemoteService extends Service {

    //客戶端綁定service時(shí)會(huì)執(zhí)行
    @Override
    public IBinder onBind(Intent intent) {
        return iBinder;
    }

    private IBinder iBinder = new IImoocAIDL.Stub(){

        @Override
        public int add(int num1, int num2) throws RemoteException {
            Log.e("TAG","收到了來自客戶端的請(qǐng)求" + num1 + "+" + num2 );
            return num1 + num2;
        }
    };
}

2.6)在AndroidMainfest.xml注冊(cè)Service

<service android:name=".IRemoteService"
            android:process=":remote"
            android:exported="true">
            <intent-filter>
                <action android:name="com.mecury.aidltest.IRomoteService"/>
            </intent-filter>
</service>

2.7)客戶端綁定服務(wù)并調(diào)用服務(wù)端方法

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private EditText num1;
    private EditText num2;
    private Button button;
    private TextView text;

    private IImoocAIDL iImoocAIDL;

    private ServiceConnection conn = new ServiceConnection() {

        //綁定服務(wù)驻民,回調(diào)onBind()方法
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            iImoocAIDL = IImoocAIDL.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            iImoocAIDL = null;
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bindService();
        initView();

    }

    private void initView() {
        num1 = (EditText) findViewById(R.id.num1);
        num2 = (EditText) findViewById(R.id.num2);
        button = (Button) findViewById(R.id.button);
        text = (TextView) findViewById(R.id.text);

        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int num11 = Integer.parseInt(num1.getText().toString());
        int num22 = Integer.parseInt(num2.getText().toString());

        try {
            int res = iImoocAIDL.add(num11,num22);
            text.setText(num11 +"+"+ num22 +"="+ res);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    private void bindService() {

        Intent intent = new Intent();
        //綁定服務(wù)端的service
        //intent.setAction("com.mecury.aidltest.IRomoteService");
        //新版本(5.0后)必須顯式intent啟動(dòng) 綁定服務(wù)
//參數(shù)1:包名,被啟動(dòng)的程序的包名
//參數(shù)2:全類名履怯,被啟動(dòng)的程序的全類名
        intent.setComponent(new ComponentName("com.mecury.aidltest","com.mecury.aidltest.IRemoteService"));
        //綁定的時(shí)候服務(wù)端自動(dòng)創(chuàng)建
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(conn);
    }
}

3回还、AIDL支持的數(shù)據(jù)類型

  • 基本數(shù)據(jù)類型(int、long虑乖、char 等)
  • String 和 CharSequence
  • List:只支持ArrayList懦趋,里面的每個(gè)元素都必須被AIDL支持。
  • Map: 只支持HashMap疹味, 里面的每個(gè)元素都必須被AIDL支持仅叫。
  • Parcelable: 所有實(shí)現(xiàn)了Parcelable接口的對(duì)象
  • AIDL: 所有的AIDL接口本身也可以在AIDL文件中使用

4帜篇、需要注意的點(diǎn)

如果客戶端和service的aidl文件是不一致的,就會(huì)出現(xiàn)問題了诫咱。
當(dāng)TestService使用新的aidl時(shí)
view plaincopy to clipboardprint?
package com.aidl.service;
interface ITestService{
int test2 ();
int test1 ();
int test3 ();
}
生成的IImoocAIDL 里面定義的TRANSACTION code如下:
view plaincopy to clipboardprint?
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);

客戶端IImoocAIDL 還使用舊的aidl笙隙,生成的IImoocAIDL 里面定義的TRANSACTION code如下:

view plaincopy to clipboardprint?
static final int TRANSACTION_test1 = (IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_test2 = (IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_test3 = (IBinder.FIRST_CALL_TRANSACTION + 2);

  從上面的兩組TRANSACTION code可以看出,TRANSACTION code是根據(jù)aidl里接口聲明的順序生成的坎缭。IBinder.FIRST_CALL_TRANSACTION的值是1竟痰,也就是說TRANSACTION_test1的值在客戶端里是1,而在service端是2! 而service端onTransact函數(shù)里的switch掏呼,當(dāng)收到的code是1的時(shí)候坏快,認(rèn)為是應(yīng)該調(diào)用TRANSACTION_test2對(duì)應(yīng)的test2方法了。所以就出現(xiàn)上面的例子中憎夷,詭異的錯(cuò)亂現(xiàn)象了莽鸿。

   所以當(dāng)aidl里面函數(shù)的聲明順序改變,或者新加拾给,刪除函數(shù)祥得,都會(huì)造成TRANSACTION code的值會(huì)不同。這樣使用舊aidl文件的應(yīng)用就可能出現(xiàn)問題蒋得!

解決方案

   當(dāng)service升級(jí)時(shí)级及,為了避免出現(xiàn)上面的問題,應(yīng)該保證aidl的變化不影響到舊有接口的TRANSACTION code额衙。所以新的aidl的編寫有以下幾個(gè)注意點(diǎn)饮焦。

新加函數(shù)接口應(yīng)該在舊有函數(shù)的后面。
盡量避免刪除舊有函數(shù)入偷,如果真的要?jiǎng)h的話追驴,可以保留函數(shù)名字作為占位,返回一個(gè)錯(cuò)誤碼之類的來解決疏之。
不能改變?cè)瓉淼慕涌诼暶黜樞颉?br> 當(dāng)然如果改變?cè)瓉砗瘮?shù)接口殿雪,導(dǎo)致函數(shù)簽名都變了的話,肯定會(huì)出錯(cuò)了锋爪,不過不是上面的錯(cuò)亂了丙曙。如果你非要這樣改的話,會(huì)被別的工程師罵精神錯(cuò)亂的!

   如果是多人協(xié)作開發(fā)其骄,使用同一個(gè)版本庫(kù)的時(shí)候亏镰,所有使用service的應(yīng)用程序,不是把a(bǔ)idl代碼cp到自己的目錄里拯爽,而是建一個(gè)文件鏈接link到service目錄里面的aidl索抓。這樣在service aidl文件有變化的時(shí)候,客戶端不需要手動(dòng)更新aidl文件。

  比如上面例子中逼肯,TestActivity的client/src/com/mecury/aidltest/目錄里面IImoocAIDL .aidl就是service/src/com/mecury/aidltest/IImoocAIDL .aidl的link耸黑。

ln -s service/src/com/mecury/aidltest/ITestService.aidl client/src/com/mecury/aidltest/IImoocAIDL .aidl

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市篮幢,隨后出現(xiàn)的幾起案子大刊,更是在濱河造成了極大的恐慌,老刑警劉巖三椿,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件缺菌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡搜锰,警方通過查閱死者的電腦和手機(jī)伴郁,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來纽乱,“玉大人蛾绎,你說我怎么就攤上這事⊙涣校” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵鹏倘,是天一觀的道長(zhǎng)薯嗤。 經(jīng)常有香客問我,道長(zhǎng)纤泵,這世上最難降的妖魔是什么骆姐? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任,我火速辦了婚禮捏题,結(jié)果婚禮上玻褪,老公的妹妹穿的比我還像新娘。我一直安慰自己公荧,他們只是感情好带射,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著循狰,像睡著了一般窟社。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上绪钥,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天灿里,我揣著相機(jī)與錄音,去河邊找鬼程腹。 笑死匣吊,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播色鸳,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼社痛,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了缕碎?” 一聲冷哼從身側(cè)響起褥影,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎咏雌,沒想到半個(gè)月后凡怎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡赊抖,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年统倒,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片氛雪。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡房匆,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出报亩,到底是詐尸還是另有隱情浴鸿,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布弦追,位于F島的核電站岳链,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏劲件。R本人自食惡果不足惜掸哑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望零远。 院中可真熱鬧苗分,春花似錦、人聲如沸牵辣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)服猪。三九已至供填,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間罢猪,已是汗流浹背近她。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留膳帕,地道東北人粘捎。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓薇缅,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親攒磨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子泳桦,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359