安卓實(shí)現(xiàn)IPC(三)—— AIDL

繼使用Intent實(shí)現(xiàn)IPC后源祈,這篇文章來學(xué)習(xí)以下使用AIDL實(shí)現(xiàn)IPC,用Intent實(shí)現(xiàn)的方法可以查看上一篇稳衬,安卓實(shí)現(xiàn)IPC(二)—— Intent。一樣的配方坐漏,一樣的套路薄疚,我們還是通過一個(gè)demo來學(xué)習(xí),先上效果圖 :

AIDL實(shí)現(xiàn)IPC

從動(dòng)畫演示來看仙畦,是從一個(gè)應(yīng)用跳轉(zhuǎn)到另一個(gè)應(yīng)用输涕,在當(dāng)中進(jìn)行了一些操作,顯示了一些數(shù)據(jù)慨畸。
這其中AndroidIPC_Demo作為AIDL中Service端莱坎,提供了數(shù)據(jù)及操作數(shù)據(jù)的方法,OtherApplication作為Client端寸士,調(diào)用服務(wù)端的方法和數(shù)據(jù)檐什,并且展示出來∪蹩ǎ客戶端還有兩個(gè)方法bindService和unbindService乃正,因?yàn)锳IDL的服務(wù)端和客戶端是通過Service組件連接的,所以這就需要我們的客戶端去綁定服務(wù)婶博,就有了這兩個(gè)方法瓮具。
具體的細(xì)節(jié)在代碼中來看,因?yàn)檫@其中涉及到包名一致凡人,引包的一些操作名党,所以先放上服務(wù)端的目錄結(jié)構(gòu) :


服務(wù)端目錄結(jié)構(gòu)

首先,建立一個(gè)實(shí)體類Book:

public class Book implements Parcelable {
    private String name;
    private int price;

    protected Book(Parcel in) {
        name = in.readString();
        price = in.readInt();
    }

    public Book(String name, int price) {
        this.name = name;
        this.price = price;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(name);
        dest.writeInt(price);
    }

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

    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];
        }
    };

    public String getName() {
        return name;
    }

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

    public int getPrice() {
        return price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

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

AIDL能傳遞一些基礎(chǔ)數(shù)據(jù)類型挠轴,有int传睹、long、boolean岸晦、float欧啤、double、String等启上,此外如果想傳遞一些自定義類型的數(shù)據(jù)邢隧,那么需要這個(gè)數(shù)據(jù)類實(shí)現(xiàn)序列化。建立好實(shí)現(xiàn)了序列化的Book類后冈在,在當(dāng)前包下創(chuàng)建一個(gè)同名的aidl文件府框,即Book.aidl文件:

//包名和Book.java的包名一樣
package com.example.computer_ren.androidipc_demo;

//Book類是自定義實(shí)體類,實(shí)現(xiàn)Parceable序列化接口,這里需要注意parcelable的首字母p是小寫
parcelable Book;

然后創(chuàng)建一個(gè)aidl文件迫靖,創(chuàng)建方法見截圖:

創(chuàng)建aidl文件

選中main目錄院峡,創(chuàng)建的aidl文件和java是同級(jí)的,創(chuàng)建一個(gè)BookManager.aidl文件:

// BookManager.aidl
package com.example.computer_ren.androidipc_demo;
//引入Book.java的包
import com.example.computer_ren.androidipc_demo.Book;

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

interface BookManager {
    /**
     * Demonstrates some basic types that you can use as parameters
     * and return values in AIDL.
     */
//    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,
//            double aDouble, String aString);
    //自己定義的方法 獲取Book列表
    List<Book> getBooks();
    //自定義的方法 增加Book
    void addBook(in Book book);
}

將剛才創(chuàng)建的Book.aidl文件移到aidl文件目錄下系宜,具體結(jié)構(gòu)見目錄結(jié)構(gòu)圖照激,至此 重新編譯一下工程,會(huì)自動(dòng)生成一個(gè)中間文件BookManager.java盹牧,這里面包含一些我們后面需要調(diào)用的方法俩垃。
至此aidl文件就編寫完畢,接著在java包下創(chuàng)建一個(gè)service汰寓,即AIDLService:

public class AIDLService extends Service {
    private List<Book> mBooks = new ArrayList<>();

    @Override
    public void onCreate() {
        super.onCreate();
        //設(shè)置數(shù)據(jù)源
        mBooks.add(new Book("第一行代碼",39));
        mBooks.add(new Book("第二行代碼",49));
        mBooks.add(new Book("Android開發(fā)藝術(shù)探索",59));
    }
    /**
      * 通過中間類BookManager的方法Stub()方法獲取Binder對(duì)象 在onBind()方法中返回這個(gè)對(duì)象 方便客戶端拿到Binder對(duì)象
      * 實(shí)現(xiàn)自定義的兩個(gè)方法 獲取書的列表和添加書
    */
    private Binder mBinder = new BookManager.Stub() {

        @Override
        public List<Book> getBooks() throws RemoteException {
            return mBooks;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            mBooks.add(book);
        }
    };

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

    @Override
    public boolean onUnbind(Intent intent) {
        Log.e("TAG", "onUnbind: " );
        return super.onUnbind(intent);
    }
}

這里用到是安卓系統(tǒng)提供的Service組件口柳,在onCreate()周期中初始化數(shù)據(jù),然后獲取到Binder對(duì)象并實(shí)現(xiàn)接口中定義的方法有滑,因?yàn)檫@里需要用到Binder對(duì)象跃闹,所以在onBind()方法中返回獲取到的Binder對(duì)象。
接著在AndroidManifest.xml中注冊(cè)Service:

        <service android:name=".AIDLService">
            <intent-filter>
                <action android:name="com.example.computer_ren.androidipc_demo.AIDLService"/>
                <category android:name="android.intent.category.DEFAULT"/>
            </intent-filter>
        </service>

至此服務(wù)端的工作已經(jīng)完成毛好,下面是客戶端的部分:
將服務(wù)端aidl文件原封不動(dòng)的復(fù)制過來望艺,目錄結(jié)構(gòu)和服務(wù)端一樣,把需要傳遞的自定義實(shí)體類也復(fù)制過來肌访,這里需要注意包名也要和服務(wù)端一致找默,具體目錄結(jié)構(gòu)如下:

客戶端目錄結(jié)構(gòu)

其中紅框內(nèi)的結(jié)構(gòu)需要注意 ,特別是包名要與服務(wù)端一致吼驶,否則編譯時(shí)會(huì)報(bào)錯(cuò)惩激。
下面是MainActivity(從服務(wù)端跳轉(zhuǎn)過來的頁面 )的代碼:

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private Button bindServiceBtn;
    private Button unbindServiceBtn;
    private Button addBookBtn;
    private Button getBooksBtn;
    private TextView showBooksTv;

    private BookManager mBookManager = null;
    private boolean isBind = false;//綁定服務(wù)的標(biāo)志符

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

        initView();
    }

    private void initView() {
        bindServiceBtn = findViewById(R.id.btn_bind);
        unbindServiceBtn = findViewById(R.id.btn_unbind);
        addBookBtn = findViewById(R.id.btn_add_book);
        getBooksBtn = findViewById(R.id.btn_get_book);
        showBooksTv = findViewById(R.id.tv_show_books);

        bindServiceBtn.setOnClickListener(this);
        unbindServiceBtn.setOnClickListener(this);
        addBookBtn.setOnClickListener(this);
        getBooksBtn.setOnClickListener(this);
    }

    ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //通過Service中的onBinder()方法返回的Binder對(duì)象獲取到中間類(BookManager.java)對(duì)象
            mBookManager = BookManager.Stub.asInterface(service);
            Toast.makeText(MainActivity.this, "綁定服務(wù)成功", Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            Log.e("TAG", "onServiceDisconnected");
        }
    };

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn_bind://綁定服務(wù)
                bindService();
                break;
            case R.id.btn_unbind://解綁服務(wù)
                unbindService();
                break;
            case R.id.btn_add_book://增加書
                addBook();
                break;
            case R.id.btn_get_book://獲取書列表
                getBooks();
                break;
        }
    }

    /**
     * 解綁服務(wù)
     */
    private void unbindService() {
        if (isBind) {
            unbindService(mServiceConnection);
            mBookManager = null;
            Toast.makeText(this, "成功解綁服務(wù)", Toast.LENGTH_SHORT).show();
            isBind = false;
        }
    }

    /**
     * 獲取書列表
     */
    private void getBooks() {
        if (!isBind) {
            Toast.makeText(this, "請(qǐng)先綁定服務(wù)", Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            List<Book> books = mBookManager.getBooks();
            showBooksTv.setText(books.toString());
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    /**
     * 增加書
     */
    private void addBook() {
        if (!isBind) {
            Toast.makeText(this, "請(qǐng)先綁定服務(wù)", Toast.LENGTH_SHORT).show();
            return;
        }
        try {
            mBookManager.addBook(new Book("西游記"+System.currentTimeMillis(),69));
            Toast.makeText(this, "添加成功", Toast.LENGTH_SHORT).show();
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    /**
     * 綁定服務(wù)
     */
    private void bindService() {
        if (!isBind) {
            //隱式啟動(dòng)遠(yuǎn)程服務(wù)
            Intent intent = new Intent();
            intent.setAction("com.example.computer_ren.androidipc_demo.AIDLService");
            intent.setPackage("com.example.computer_ren.androidipc_demo");
            bindService(intent,mServiceConnection, Context.BIND_AUTO_CREATE);
            isBind = true;
        }
    }
}

這部分代碼主要說三個(gè)點(diǎn):

1、用intent隱式啟動(dòng)遠(yuǎn)程服務(wù)蟹演,調(diào)用方法bindService()綁定服務(wù)风钻;
2、在綁定服務(wù)的時(shí)候需要傳入一個(gè)ServiceConnection對(duì)象轨帜,在創(chuàng)建這個(gè)對(duì)象的同時(shí)我們可以獲取到從Service的onBind()方法中返回的Binder對(duì)象魄咕;
3衩椒、拿到了Binder對(duì)象蚌父,我們就可以通過aidl文件生成的中間對(duì)象調(diào)用方法Stub和asInterface方法拿到中間類對(duì)象。

通過以上方法拿到中間類對(duì)象后毛萌,就能調(diào)用我們?cè)诜?wù)端自定義的方法了苟弛,也就實(shí)現(xiàn)了進(jìn)程間通信。
其余都是一些簡單的業(yè)務(wù)代碼阁将,代碼中也給了詳細(xì)的注釋膏秫,這里就不做介紹了。

好了做盅,以上就是用AIDL實(shí)現(xiàn)IPC的整個(gè)過程缤削,過程有點(diǎn)復(fù)雜窘哈,所以這里做個(gè)總結(jié):

服務(wù)端:
1、傳遞的數(shù)據(jù)類型為自定義時(shí)亭敢,需要實(shí)現(xiàn)序列化接口滚婉,并且還需要?jiǎng)?chuàng)建一個(gè)同名的aidl文件;
2帅刀、再創(chuàng)建一個(gè)aidl接口文件让腹,在里面定義一些方法,供客戶端調(diào)用扣溺,重新編譯一下工程會(huì)自動(dòng)生成一個(gè)對(duì)應(yīng)的中間類骇窍;
3、在Service組件中通過aidl生成的中間類的Stub()方法獲得Binder對(duì)象锥余,并且實(shí)現(xiàn)接口中自定義的方法腹纳,Binder對(duì)象在onBind()方法中返回供客戶端調(diào)用;
4哈恰、在AndroidManifest.xml中注冊(cè)service只估。
客戶端:
1、將服務(wù)端的aidl文件和需要在進(jìn)程間傳遞的實(shí)體類原封不動(dòng)的復(fù)制過來着绷,這里需要注意實(shí)體類的包名也要與服務(wù)端一致蛔钙;
2、通過Intent隱式啟動(dòng)遠(yuǎn)程服務(wù)并綁定服務(wù)荠医;
3吁脱、用ServiceConnection這個(gè)類來獲取遠(yuǎn)程服務(wù)中Binder對(duì)象,然后調(diào)用中間類的方法Stub和asInterface()將Binder對(duì)象傳進(jìn)去獲得中間類對(duì)象彬向,得到中間類對(duì)象就能調(diào)用自己定義的接口方法了

對(duì)AIDL的學(xué)習(xí)到此為止兼贡。基于AIDL還有一種方法也能實(shí)現(xiàn)進(jìn)程間通信娃胆,那就是Messenger遍希,它是對(duì)AIDL的封裝,但存在一定的局限性:

Messenger存在的局限性:
1里烦、Messenger 是以串行的方式處理接受的消息凿蒜,即使有大量的消息同時(shí)到達(dá),也只能一個(gè)個(gè)處理胁黑,所以 Messenger 不適合用于處理大量的并發(fā)請(qǐng)求废封;
2、Messenger是用Hander來發(fā)送消息丧蘸,傳遞的數(shù)據(jù)放在Message這個(gè)類中漂洋,所以只能實(shí)現(xiàn)兩個(gè)進(jìn)程間的數(shù)據(jù)通信,而無法實(shí)現(xiàn)一個(gè)進(jìn)程調(diào)用另一個(gè)進(jìn)程的方法 。

Messenger是對(duì)AIDL的封裝刽漂,所以開發(fā)成本相對(duì)較低演训,在不考慮上面提到的局限時(shí),可以考慮用Messenger來實(shí)現(xiàn)IPC贝咙,這其中的選擇根據(jù)開發(fā)業(yè)務(wù)而定仇祭。

學(xué)習(xí)了AIDL,下一篇將學(xué)習(xí)用Broadcast組件來實(shí)現(xiàn)IPC颈畸。

安卓實(shí)現(xiàn)IPC(四)—— Broadcast

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末乌奇,一起剝皮案震驚了整個(gè)濱河市眯娱,隨后出現(xiàn)的幾起案子礁苗,更是在濱河造成了極大的恐慌,老刑警劉巖徙缴,帶你破解...
    沈念sama閱讀 216,919評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件试伙,死亡現(xiàn)場離奇詭異,居然都是意外死亡于样,警方通過查閱死者的電腦和手機(jī)疏叨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,567評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來穿剖,“玉大人蚤蔓,你說我怎么就攤上這事『啵” “怎么了秀又?”我有些...
    開封第一講書人閱讀 163,316評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贬芥。 經(jīng)常有香客問我吐辙,道長,這世上最難降的妖魔是什么蘸劈? 我笑而不...
    開封第一講書人閱讀 58,294評(píng)論 1 292
  • 正文 為了忘掉前任昏苏,我火速辦了婚禮,結(jié)果婚禮上威沫,老公的妹妹穿的比我還像新娘贤惯。我一直安慰自己,他們只是感情好壹甥,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,318評(píng)論 6 390
  • 文/花漫 我一把揭開白布救巷。 她就那樣靜靜地躺著壶熏,像睡著了一般句柠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,245評(píng)論 1 299
  • 那天溯职,我揣著相機(jī)與錄音精盅,去河邊找鬼。 笑死谜酒,一個(gè)胖子當(dāng)著我的面吹牛叹俏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播僻族,決...
    沈念sama閱讀 40,120評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼粘驰,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了述么?” 一聲冷哼從身側(cè)響起蝌数,我...
    開封第一講書人閱讀 38,964評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎度秘,沒想到半個(gè)月后顶伞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,376評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡剑梳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,592評(píng)論 2 333
  • 正文 我和宋清朗相戀三年唆貌,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片垢乙。...
    茶點(diǎn)故事閱讀 39,764評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡锨咙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出追逮,到底是詐尸還是另有隱情蓖租,我是刑警寧澤,帶...
    沈念sama閱讀 35,460評(píng)論 5 344
  • 正文 年R本政府宣布羊壹,位于F島的核電站蓖宦,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏油猫。R本人自食惡果不足惜稠茂,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,070評(píng)論 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望情妖。 院中可真熱鬧睬关,春花似錦、人聲如沸毡证。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,697評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽料睛。三九已至丐箩,卻和暖如春摇邦,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背屎勘。 一陣腳步聲響...
    開封第一講書人閱讀 32,846評(píng)論 1 269
  • 我被黑心中介騙來泰國打工施籍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人概漱。 一個(gè)月前我還...
    沈念sama閱讀 47,819評(píng)論 2 370
  • 正文 我出身青樓丑慎,卻偏偏與公主長得像,于是被迫代替她去往敵國和親瓤摧。 傳聞我的和親對(duì)象是個(gè)殘疾皇子竿裂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,665評(píng)論 2 354

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