android中的IPC方式

使用Bundle

四大組件中的三大組件(Activity、Service、Receiver)都是支持在Intent中傳遞Bundle數(shù)據(jù)的。
所以我們?cè)谝粋€(gè)進(jìn)程中啟動(dòng)了另外一個(gè)進(jìn)程的Activity、Service奈辰、Receiver,我們就可以在Bundle中附加我們需要傳輸給遠(yuǎn)程進(jìn)程的信息并通過Intent發(fā)送出去乱豆。

Intent intent = new Intent(BundleFirstActivity.this, BundleSecondActivity.class);
intent.putExtra("User", new User("haha", 222));
startActivity(intent);

特殊場(chǎng)景:
當(dāng)A進(jìn)程計(jì)算出來了一個(gè)結(jié)果奖恰,需要傳輸給B進(jìn)程,但是傳輸?shù)臄?shù)據(jù)不支持放入Bundle中,因此無法通過Intent來傳輸瑟啃。這個(gè)時(shí)候我們可以通過Intent啟動(dòng)進(jìn)程B的一個(gè)Service組件(比如IntentService)论泛,讓Service在后臺(tái)計(jì)算,計(jì)算完成后再啟動(dòng)B進(jìn)程中真要需要的目標(biāo)組件蛹屿。

使用文件共享

共享文件也是一種不錯(cuò)的進(jìn)程間通信方式屁奏,兩個(gè)進(jìn)程通過讀/寫同一個(gè)文件來交換數(shù)據(jù),比如A進(jìn)程把數(shù)據(jù)寫入文件错负,B進(jìn)程通過讀取這個(gè)文件來獲取數(shù)據(jù)坟瓢。

需要注意的是并發(fā)情況下的讀/寫,可能造成數(shù)據(jù)錯(cuò)亂犹撒。
可能有人會(huì)想到SharedPreferences载绿,但是SharedPreferences是個(gè)特例。從本質(zhì)上來說油航,SharedPreferences也屬于文件的一種,但是由于系統(tǒng)對(duì)它的讀/寫有一定的緩存策略怀浆,即在內(nèi)存中會(huì)有一個(gè)SharedPreferences文件的緩存谊囚,因此在多進(jìn)程模式下,系統(tǒng)對(duì)它的讀/寫就變得不可靠执赡,當(dāng)面對(duì)高并發(fā)的讀/寫訪問镰踏,SharedPreferences有很大幾率會(huì)丟失數(shù)據(jù),因此沙合,不建議在進(jìn)程間通信中使用SharedPreferences奠伪。
每個(gè)應(yīng)用的SharedPreferences文件都可以在當(dāng)前包所在的data目錄下查看到。一般來說首懈,它的目錄位于/data/data/package name/share_prefs目錄下绊率。

使用Messenger

Messenger是一種輕量級(jí)的IPC方案,它的底層實(shí)現(xiàn)是AIDL究履。

由于它一次處理一個(gè)請(qǐng)求滤否,因此在服務(wù)端我們不用考慮線程同步的問題,這是因?yàn)榉?wù)端中不存在并發(fā)執(zhí)行的情形最仑。正是因?yàn)镸essenger是以串行的方式處理客戶端發(fā)來的消息藐俺,如果大量的消息同時(shí)發(fā)送到服務(wù)端,服務(wù)端仍然只能一個(gè)個(gè)處理泥彤,如果有大量的并發(fā)請(qǐng)求欲芹,那么用Messenger就不太合適 了。同時(shí)吟吝,Messenger的作用主要是為了傳遞消息菱父,很多時(shí)候我們可能需要跨進(jìn)程調(diào)用服務(wù)端的方法,這種情形用Messenger就無法做到了。

使用AIDL(Android Interface Defined Language(安卓接口定義語言))

如上所說Messenger無法完成跨進(jìn)程調(diào)用服務(wù)端的方法滞伟,而且不能處理并發(fā)請(qǐng)求揭鳞。但是我們可以使用AIDL來實(shí)現(xiàn)跨進(jìn)程的方法調(diào)用。AIDL也是Messenger的底層實(shí)現(xiàn)梆奈,因此Messenger本質(zhì)上也是AIDL野崇,只不過系統(tǒng)為我們做了封裝從而方便上層的調(diào)用而已。

1.服務(wù)端
服務(wù)端首先要?jiǎng)?chuàng)建一個(gè)Service用來監(jiān)聽客戶端的連接請(qǐng)求亩钟,然后創(chuàng)建一個(gè)AIDL文件乓梨,將暴露給客戶端的接口在這個(gè)AIDL文件中聲明,最后在Service中實(shí)現(xiàn)這個(gè)AIDL接口即可清酥。

2.客戶端
首先需要綁定服務(wù)端的Service扶镀,然后將服務(wù)端返回的Binder對(duì)象轉(zhuǎn)成AIDL接口所屬類型,最后調(diào)用AIDL中的方法焰轻。

3.AIDL接口的創(chuàng)建
默認(rèn)情況下臭觉,AIDL 支持下列數(shù)據(jù)類型:

  • Java 編程語言中的所有原語類型(如 int、long辱志、char蝠筑、boolean 等等)
  • String
  • CharSequence
  • List
    List 中的所有元素都必須是以上列表中支持的數(shù)據(jù)類型、其他 AIDL 生成的接口或您聲明的可打包類型揩懒。 可選擇將 List 用作“通用”類(例如什乙,List<String>)。另一端實(shí)際接收的具體類始終是 ArrayList已球,但生成的方法使用的是 List 接口臣镣。
  • Map
    Map 中的所有元素都必須是以上列表中支持的數(shù)據(jù)類型、其他 AIDL 生成的接口或您聲明的可打包類型智亮。 不支持通用 Map(如 Map<String,Integer> 形式的 Map)忆某。 另一端實(shí)際接收的具體類始終是 HashMap,但生成的方法使用的是 Map 接口阔蛉。
  • Parcelable
    所有實(shí)現(xiàn)了Parcelable接口的對(duì)象褒繁。

注意: 如果AIDL文件中用到了自定義的Parcelable對(duì)象,那么必須新建一個(gè)和它同名的AIDL文件馍忽,并在其中聲明它為Parcelable類型棒坏。

// Book.aidl
package com.whyalwaysmea.ipc;

parcelable Book;

4.遠(yuǎn)程服務(wù)端Service的實(shí)現(xiàn)

public class BookManagerService extends Service {

    // CopyOnWriteArrayList支持并發(fā)讀/寫
    // 因?yàn)锳IDL方法是在服務(wù)端的Binder線程池中執(zhí)行的,因此當(dāng)多個(gè)客戶端同時(shí)連接的時(shí)候遭笋,會(huì)存在多個(gè)線程同時(shí)訪問的情形
    // 所以我們要在AIDL方法中處理線程同步
    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<>();

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

    private Binder mBinder = new IBookManager.Stub(){

        @Override
        public List<Book> getBookList() throws RemoteException {
            return mBookList;
        }

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

    @Override
    public void onCreate() {
        super.onCreate();
    }
}

5.客戶端的實(shí)現(xiàn)
首先要綁定遠(yuǎn)程服務(wù)坝冕,綁定成功后將服務(wù)端返回的Binder對(duì)象轉(zhuǎn)換成AIDL接口,然后就可以通過這個(gè)接口去調(diào)用服務(wù)端的遠(yuǎn)程方法了瓦呼。

public class BookManagerActivity extends AppCompatActivity {

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            IBookManager bookManager = IBookManager.Stub.asInterface(service);
            try {
                List<Book> bookList = bookManager.getBookList();
                System.out.println("book  list type : " + bookList.getClass().getCanonicalName());
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_book_manager);
        Intent intent = new Intent(this, BookManagerService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

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

可能有人會(huì)發(fā)現(xiàn)喂窟,在Service中测暗,我們返回的數(shù)據(jù)類型是CopyOnWriteArrayList,但是到了Activity中磨澡,接收到了卻是List碗啄。這是為什么呢?

AIDL中能夠使用的List只有ArrayList稳摄,但是我們這里卻使用了CopyOnWriteArrayList(注意它并不是繼承自ArrayList)稚字。這是因?yàn)锳IDL中所支持的是抽象的List,而List只是一個(gè)接口厦酬,因此雖然服務(wù)端返回的是CopyOnWriteArrayList胆描,但是在Binder中會(huì)按照List的規(guī)范去訪問數(shù)據(jù)并最終形成一個(gè)ArrayList傳遞給客戶端。

RemoteCallbackList

RemoteCallbackList是系統(tǒng)專門提供的用于刪除跨進(jìn)程Listener的接口仗阅。RemoteCallbackList是一個(gè)泛型昌讲,支持管理任意的AIDL接口,因?yàn)樗械腁IDL接口都繼承自IInterface接口减噪。

注意

客戶端調(diào)用遠(yuǎn)程服務(wù)的方法短绸,被調(diào)用的方法運(yùn)行在服務(wù)端的Binder線程池中,同時(shí)客戶端線程會(huì)被掛起筹裕,這個(gè)時(shí)候如果服務(wù)端方法執(zhí)行比較耗時(shí)醋闭,就會(huì)導(dǎo)致客戶端線程長時(shí)間地阻塞,如果這個(gè)客戶端線程是UI線程的話饶碘,就會(huì)導(dǎo)致客戶端ANR。所以馒吴,如果我們明確知道某個(gè)遠(yuǎn)程方法是耗時(shí)的扎运,那么就要避免在客戶端的UI線程中去訪問遠(yuǎn)程的耗時(shí)方法。

同理饮戳,當(dāng)遠(yuǎn)程服務(wù)端需要調(diào)用客戶端的方法時(shí)豪治,被調(diào)用的方法也運(yùn)行在Binder線程池中,只不過是客戶端的線程池扯罐。所以负拟,我們同樣不可以在服務(wù)端中調(diào)用客戶端的耗時(shí)方法。

Binder是可能意外死亡的歹河,由于服務(wù)端進(jìn)程意外停止了掩浙,這時(shí)我們需要重新連接服務(wù)。

  1. 給Binder設(shè)備DeathRecipient監(jiān)聽
  2. 在onServiceDisconnected中重連遠(yuǎn)程服務(wù)秸歧。

兩者的區(qū)別是:onServiceDisconnected在客戶端的UI線程中被回調(diào)厨姚,而binderDied在客戶端的Binder線程池中被回調(diào)。

權(quán)限驗(yàn)證

默認(rèn)情況下键菱,我們的遠(yuǎn)程服務(wù)任何人都可以連接谬墙,但這以你哥哥不是我們?cè)敢饪吹降模晕覀儽仨毤尤霗?quán)限驗(yàn)證功能。

1.在onBind中進(jìn)行驗(yàn)證拭抬,驗(yàn)證不通過就直接返回null部默。比如使用permission驗(yàn)證

<permission
        android:name="com.whyalwaysmea.permission.ACCESS_BOOK_SERVICE"
        android:protectionLevel="normal" />

public IBinder onbind(Intent intent) {
    int check = checkCallingOrSelfPermission("com.whyalwaysmea.permission.ACCESS_BOOK_SERVICE");
    if(check == PackageManager.PERMISSION_DENIED) {
        return null;
    }
    return mBinder;
}        

如果我們自己的應(yīng)用想綁定到服務(wù)上,只需要在它的AndroidMenifest文件中采用如下方式使用permission

<uses-permission android:name="com.whyalwaysmea.permission.ACCESS_BOOK_SERVICE" />

2.我們可以在服務(wù)端的onTransact方法中進(jìn)行權(quán)限驗(yàn)證造虎,如果驗(yàn)證失敗就返回false傅蹂,這樣服務(wù)端就不會(huì)終止執(zhí)行AIDL中的方法從而達(dá)到保護(hù)客戶端的效果。這里可以驗(yàn)證的方式很多累奈,比如permission贬派,Uid,Pid等驗(yàn)證澎媒。

使用ContentProvider

ContentProvider是Android中提供的專門用于不同應(yīng)用間進(jìn)行數(shù)據(jù)共享的方式搞乏,它的底層實(shí)現(xiàn)同樣也是Binder。

系統(tǒng)預(yù)置了許多ContentProvider戒努,比如通訊錄请敦、日程表等,要跨進(jìn)程訪問這些信息储玫,只需要通過ContentProvider的query侍筛、update、insert和delete方法即可撒穷。

關(guān)于自定義ContentProvider的過程:

1.創(chuàng)建一個(gè)自定義的ContentProvider匣椰,名字就叫BookProvider,只需要繼承ContentProvider類并實(shí)現(xiàn)六個(gè)抽象方法即可:onCreate端礼、query禽笑、update、insert蛤奥、delete和geType佳镜。除了onCreate由系統(tǒng)回調(diào)并運(yùn)行在主線程里鬼癣,其他五個(gè)方法均由外界回調(diào)并運(yùn)行在Binder線程池中玉锌。

2.注冊(cè)ContentProvider。

<provider
    // ContentProvider的唯一標(biāo)識(shí)翔横,通過這個(gè)屬性外部應(yīng)用就可以訪問我們的BookProvider
    android:authorities="com.whyalwaysmea.ipc.provider.BookProvider"
    android:name=".provider.BookProvider"
    android:permission="com.whyalwaysmea.provider"  // 權(quán)限
    android:process=":provider"/>

3.通過外部應(yīng)用訪問BookProvider缅刽。

注意

  1. ContentProvider主要以表格的形式來組織數(shù)據(jù)啊掏,并且可以包含多個(gè)表;
  2. ContentProvider還支持文件數(shù)據(jù)衰猛,比如圖片脖律、視頻等,系統(tǒng)提供的MediaStore就是文件類型的ContentProvider腕侄;
  3. ContentProvider對(duì)底層的數(shù)據(jù)存儲(chǔ)方式?jīng)]有任何要求小泉,可以是SQLite芦疏、文件,甚至是內(nèi)存中的一個(gè)對(duì)象都行微姊;
  4. 要觀察ContentProvider中的數(shù)據(jù)變化情況酸茴,可以通過ContentResolver的registerContentObserver方法來注冊(cè)觀察者;
  5. query,update,insert,delete四大方法是存在多線程并發(fā)訪問的兢交,因此方法內(nèi)部要做好線程同步薪捍。

使用Socket

Binder連接池

當(dāng)項(xiàng)目規(guī)模很大的時(shí)候,創(chuàng)建很多個(gè)Service是不對(duì)的做法配喳,因?yàn)閟ervice是系統(tǒng)資源酪穿,太多的service會(huì)使得應(yīng)用看起來很重,所以最好是將所有的AIDL放在同一個(gè)Service中去管理晴裹。
整個(gè)工作機(jī)制是:每個(gè)業(yè)務(wù)模塊創(chuàng)建自己的AIDL接口并實(shí)現(xiàn)此接口被济,這個(gè)時(shí)候不同業(yè)務(wù)模塊之間是不能有耦合的,所有實(shí)現(xiàn)細(xì)節(jié)我們要單獨(dú)開來涧团,然后向服務(wù)端提供自己的唯一標(biāo)識(shí)和其對(duì)應(yīng)的Binder對(duì)象只磷;對(duì)于服務(wù)端來說,只需要一個(gè)Service泌绣,服務(wù)端提供一個(gè)queryBinder接口钮追,這個(gè)接口能夠根據(jù)業(yè)務(wù)模塊的特征來返回相應(yīng)的Binder對(duì)象給它們,不同的業(yè)務(wù)模塊拿到所需的Binder對(duì)象后就可以進(jìn)行遠(yuǎn)程方法調(diào)用了阿迈。

選擇合適的IPC方式

選擇合適的IPC方式
選擇合適的IPC方式
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末元媚,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子苗沧,更是在濱河造成了極大的恐慌刊棕,老刑警劉巖,帶你破解...
    沈念sama閱讀 223,207評(píng)論 6 521
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件崎页,死亡現(xiàn)場(chǎng)離奇詭異鞠绰,居然都是意外死亡腰埂,警方通過查閱死者的電腦和手機(jī)飒焦,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,455評(píng)論 3 400
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來屿笼,“玉大人牺荠,你說我怎么就攤上這事÷恳唬” “怎么了休雌?”我有些...
    開封第一講書人閱讀 170,031評(píng)論 0 366
  • 文/不壞的土叔 我叫張陵,是天一觀的道長肝断。 經(jīng)常有香客問我杈曲,道長驰凛,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,334評(píng)論 1 300
  • 正文 為了忘掉前任担扑,我火速辦了婚禮恰响,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘涌献。我一直安慰自己胚宦,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,322評(píng)論 6 398
  • 文/花漫 我一把揭開白布燕垃。 她就那樣靜靜地躺著枢劝,像睡著了一般。 火紅的嫁衣襯著肌膚如雪卜壕。 梳的紋絲不亂的頭發(fā)上您旁,一...
    開封第一講書人閱讀 52,895評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音印叁,去河邊找鬼被冒。 笑死,一個(gè)胖子當(dāng)著我的面吹牛轮蜕,可吹牛的內(nèi)容都是我干的昨悼。 我是一名探鬼主播,決...
    沈念sama閱讀 41,300評(píng)論 3 424
  • 文/蒼蘭香墨 我猛地睜開眼跃洛,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼率触!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起汇竭,我...
    開封第一講書人閱讀 40,264評(píng)論 0 277
  • 序言:老撾萬榮一對(duì)情侶失蹤葱蝗,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后细燎,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體两曼,經(jīng)...
    沈念sama閱讀 46,784評(píng)論 1 321
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,870評(píng)論 3 343
  • 正文 我和宋清朗相戀三年玻驻,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了悼凑。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,989評(píng)論 1 354
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡璧瞬,死狀恐怖户辫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情嗤锉,我是刑警寧澤渔欢,帶...
    沈念sama閱讀 36,649評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站瘟忱,受9級(jí)特大地震影響奥额,放射性物質(zhì)發(fā)生泄漏苫幢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,331評(píng)論 3 336
  • 文/蒙蒙 一垫挨、第九天 我趴在偏房一處隱蔽的房頂上張望态坦。 院中可真熱鬧,春花似錦棒拂、人聲如沸伞梯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,814評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽谜诫。三九已至,卻和暖如春攻旦,著一層夾襖步出監(jiān)牢的瞬間喻旷,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,940評(píng)論 1 275
  • 我被黑心中介騙來泰國打工牢屋, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留且预,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,452評(píng)論 3 379
  • 正文 我出身青樓烙无,卻偏偏與公主長得像锋谐,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子截酷,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,995評(píng)論 2 361

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

  • 前面我們介紹了Serializable和Parcelable的基礎(chǔ)知識(shí)涮拗,以及Binder的相關(guān)內(nèi)容。本篇迂苛,開始分析...
    hds2007閱讀 288評(píng)論 0 0
  • 1. AIDL簡(jiǎn)介 Messenger是串行處理客戶端發(fā)來的消息三热,如果大量的消息同時(shí)發(fā)送到服務(wù)端,服務(wù)端只能一個(gè)個(gè)...
    武安長空閱讀 336評(píng)論 0 0
  • 1.Android中的多進(jìn)程模式: 在android中最有特色的進(jìn)程通訊方式就是Binder了三幻,通過Binder可...
    jian的簡(jiǎn)書閱讀 429評(píng)論 0 1
  • Android跨進(jìn)程通信IPC整體內(nèi)容如下 1就漾、Android跨進(jìn)程通信IPC之1——Linux基礎(chǔ)2、Andro...
    隔壁老李頭閱讀 3,622評(píng)論 3 12
  • 一念搬、Android IPC簡(jiǎn)介 IPC是Inter-Process Communication的縮寫抑堡,含義就是進(jìn)程...
    SeanMa閱讀 1,835評(píng)論 0 8