Android藝術(shù)探索學(xué)習(xí)筆記:第2章 IPC機(jī)制

1.Android IPC簡(jiǎn)介

  • IPC為進(jìn)程間通訊,兩個(gè)進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過(guò)程。
  • IPC不是Android所獨(dú)有的,任何一個(gè)操作系統(tǒng)都有對(duì)應(yīng)的IPC機(jī)制鲁僚。Windows上通過(guò)剪切板、管道裁厅、油槽等進(jìn)行進(jìn)程間通訊冰沙。Linux上通過(guò)命名空間、共享內(nèi)容执虹、信號(hào)量等進(jìn)行進(jìn)程間通訊拓挥。Android中沒(méi)有完全繼承于Linux,有特色的進(jìn)程間通訊方式是Binder(基于BInder的Messenger和AIDL)袋励,還支持Socket侥啤。
  • 使用場(chǎng)景:由于某些原因應(yīng)用自身需要采用多進(jìn)程模式來(lái)實(shí)現(xiàn)当叭,或者為了加大一個(gè)應(yīng)用可使用的內(nèi)存,因?yàn)锳ndroid對(duì)當(dāng)個(gè)應(yīng)用可使用的最大內(nèi)存做了限制盖灸。

2.Android中的多進(jìn)程模式

  • 同一個(gè)應(yīng)用蚁鳖,通過(guò)給四大組件指定android:process屬性,就可以開(kāi)啟多進(jìn)程模式赁炎。
  • 進(jìn)程名以":"開(kāi)頭的屬于當(dāng)前應(yīng)用的私有進(jìn)程醉箕,其他應(yīng)用的組件不可以和他跑在同一個(gè)進(jìn)程里面。而進(jìn)程名不以":"開(kāi)頭的進(jìn)程屬于全局進(jìn)程徙垫,其他應(yīng)用通過(guò)ShareUID方式可以和它跑在同一個(gè)進(jìn)程中讥裤。兩個(gè)應(yīng)用可以通過(guò)ShareUID跑在同一個(gè)進(jìn)程并且簽名相同,他們可以共享data目錄姻报、組件信息己英、共享內(nèi)存數(shù)據(jù)。
  • 多進(jìn)程通訊的問(wèn)題:
    1.靜態(tài)成員和單例模式完全失效吴旋。
    2.線程同步機(jī)制完全失效损肛。
    3.SharedPreferences的可靠性下降
    4.Application會(huì)多次創(chuàng)建

問(wèn)題1、2原因是因?yàn)檫M(jìn)程不同邮府,已經(jīng)不是同一塊內(nèi)存了;
問(wèn)題3 是因?yàn)镾haredPreferences不支持兩個(gè)進(jìn)程同時(shí)進(jìn)行讀寫(xiě)操作溉奕,有一定幾率導(dǎo)致數(shù)據(jù)丟失褂傀;
問(wèn)題4 是當(dāng)一個(gè)組件跑在一個(gè)新的進(jìn)程中,系統(tǒng)會(huì)為他創(chuàng)建新的進(jìn)程同時(shí)分配獨(dú)立的虛擬機(jī)加勤,所有這個(gè)過(guò)程其實(shí)就是啟動(dòng)一個(gè)應(yīng)用的過(guò)程仙辟,,因此相當(dāng)于系統(tǒng)又把這個(gè)應(yīng)用重新啟動(dòng)了一遍鳄梅,Application也是新建了叠国。
實(shí)現(xiàn)跨進(jìn)程通訊有很多方式共享文件、SharedPreferences戴尸、基于Binder的Messenger和AIDL粟焊、Socket等。

3. IPC基礎(chǔ)概念介紹

1.Serializable

  • serialVersionUID是用來(lái)輔助序列化和反序列化過(guò)程的孙蒙,原則上序列化后的數(shù)據(jù)中的serialVersionUID要和當(dāng)前類的serialVersionUID相同才能正常的序列化,(當(dāng)類結(jié)構(gòu)改變不能反序列化)项棠。
  • 靜態(tài)成員變量屬于類不屬于對(duì)象,所以不會(huì)參加序列化過(guò)程挎峦;其次用transient關(guān)鍵字標(biāo)明的成員變量也不參加序列化過(guò)程香追。
  • 重寫(xiě)如下兩個(gè)方法可以重寫(xiě)系統(tǒng)默認(rèn)的序列化和反序列化過(guò)程
private void writeObject(java.io.ObjectOutputStream out)throws IOException{
}
private void readObject(java.io.ObjectInputStream out)throws IOException,ClassNotFoundException{
}

2.Parcable

Serializable是Java中的序列化接口,需要大量的I/O操作坦胶,開(kāi)銷大透典,Parcable是Android的序列化方式晴楔,更適合Android,效率高峭咒,(如果涉及到持久化保存税弃,建議使用Serializable,Parcable版本不同讹语,可能會(huì)發(fā)生變化)钙皮。

public class Book implements Parcelable {
 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 int code;
 public String name;
 public Book(int code, String name) {
     this.code = code;
     this.name = name;
 }
 private Book(Parcel in) {
     code = in.readInt();
     name = in.readString();
 }
 public int describeContents() {
     return 0;
 }
 @Override
 public void writeToParcel(Parcel dest, int flags) {
     dest.writeInt(code);
     dest.writeString(name);
 }
}
      序列化功能由writeToParcel方法來(lái)完成,最終通過(guò)Parcel中的一系列write方法完成的顽决。  
      反序列化功能由CREATEOR來(lái)完成短条,其內(nèi)部標(biāo)明了如何創(chuàng)建序列號(hào)對(duì)象和訴諸,  
      并通過(guò)Parcel的一系列read方法來(lái)完成反序列化過(guò)程才菠。內(nèi)容描述功能由describeContents方法來(lái)完成  
    茸时,幾乎所有情況都返回0,只有當(dāng)前對(duì)象存在文件描述符時(shí)赋访,才返回1可都。

3.Binder(例子

AIDL的本質(zhì)是系統(tǒng)為我們提供了一個(gè)實(shí)現(xiàn)Binder的工具,僅此而已蚓耽。
源碼分析見(jiàn)書(shū)

1.從IPC的角度來(lái)說(shuō)渠牲,Binder是Android的一種跨進(jìn)程的通訊方式;Binder也可以理解為是一種虛擬額物理設(shè)備步悠,他的設(shè)備驅(qū)動(dòng)是/dev/binder签杈;
2.從Android Framework角度來(lái)說(shuō),Binder是ServiceManager連接各種Manager(ActivityManager鼎兽、WindowManager答姥、等等)和ManagerService的橋梁;
3.從Android應(yīng)用層來(lái)說(shuō)谚咬,Binder是客戶端與服務(wù)端通訊的媒介鹦付。在Android開(kāi)發(fā)中,Binder主要用于Service中择卦,包括AIDL和Messenger敲长,其中普通的Service的Binder不涉及進(jìn)程間通訊;而Messenger的底層其實(shí)就是AIDL秉继。

Binder死亡時(shí)重連服務(wù)

1.設(shè)置死亡代理判斷Binder是否死亡

Binder的兩個(gè)重要方法linkToDeath和unlinkToDeath潘明。通過(guò)linkToDeath可以給Binder設(shè)置一個(gè)死亡代理,當(dāng)Binder死亡時(shí)秕噪,我們就會(huì)收到通知钳降,然后就可以重新發(fā)起連接請(qǐng)求。聲明一個(gè)DeathRecipient對(duì)象腌巾,DeathRecipient是一個(gè)接口遂填,其內(nèi)部只有一個(gè)方法binderDied铲觉,實(shí)現(xiàn)這個(gè)方法后就可以在Binder死亡的時(shí)候收到通知了。

private IBinder.DeathRecipient mDeathRecipient = new IBinder.DeathRecipient(){
@Override
public void binderDied(){
    if(mBookManager == null){
        return;
    }
    mBookManager.asBinder().unlinkToDeath(mDeathRecipient,0);
    mBookManager = null;
    // TODO:接下來(lái)重新綁定遠(yuǎn)程Service
}
}

在客戶端綁定遠(yuǎn)程服務(wù)成功后吓坚,給Binder設(shè)置死亡代理

mService = IBookManager.Stub.asInterface(binder);
binder.linkToDeath(mDeathRecipient,0);

2.在onServiceDisconnected重連遠(yuǎn)程服務(wù)

區(qū)別:binderDied是在客戶端的Binder線程中回調(diào)撵幽,不能訪問(wèn)UI,而onServiceDisconnected可以訪問(wèn)UI礁击。

源碼示例
方法介紹:

1.DESCRIPTOR: Binder的唯一標(biāo)識(shí)盐杂,一般用當(dāng)前Binder的類名表示。
2.asInterface(android.os.IBinder obj): 將服務(wù)端的Binder對(duì)象轉(zhuǎn)換成客戶端所需要的AIDL接口類型的對(duì)象哆窿;  
  如果客戶端和服務(wù)端位于相同進(jìn)程链烈,那么此方法返回的就是服務(wù)端Stub對(duì)象本身,否則返回系統(tǒng)封裝后的Stub.proxy對(duì)象挚躯。
3.asBinder :用于返回當(dāng)前的Binder對(duì)象
4.onTransact 運(yùn)行在服務(wù)端的Binder線程池中强衡,當(dāng)客戶端發(fā)起跨進(jìn)程通訊時(shí),遠(yuǎn)程請(qǐng)求會(huì)通過(guò)系統(tǒng)底層封裝交由此方法處理码荔。
    public Boolean onTransact(int code,Parcelable data,Parcelable reply,int flags)
服務(wù)端通過(guò)code確認(rèn)客戶端請(qǐng)求的目標(biāo)方法是什么漩勤,接著從data中取出目標(biāo)方法所需的參數(shù)(如果有),然后執(zhí)行目標(biāo)方法缩搅。  
當(dāng)目標(biāo)方法執(zhí)行完后越败,向reply中寫(xiě)入返回值(如果有)。如果方法返回值為false硼瓣,那么服務(wù)端的請(qǐng)求會(huì)失敗究飞,利用這個(gè)特性我們可以來(lái)做權(quán)限驗(yàn)證。

4.Android中的IPC方式

1.使用Bundle:

由于Bundle實(shí)現(xiàn)了Parcelable接口巨双,所以可以方便的在不同進(jìn)程中傳輸噪猾;Activity霉祸、Service和Receiver都支持在Intent中傳遞Bundle數(shù)據(jù)筑累。

2.使用文件共享:

兩個(gè)進(jìn)程通過(guò)讀/寫(xiě)一個(gè)文件來(lái)交換數(shù)據(jù);適合對(duì)數(shù)據(jù)同步要求性不高的場(chǎng)景丝蹭;并要避免并發(fā)寫(xiě)這種場(chǎng)景或者處理好線程同步問(wèn)題。SharedPreferences是個(gè)特例奔穿,雖然也是文件的一種,但系統(tǒng)在內(nèi)存中有一份SharedPreferences文件的緩存贱田,因此在多線程模式下,系統(tǒng)對(duì)他的讀/寫(xiě)就變得不可靠男摧,高并發(fā)讀寫(xiě)SharedPreferences有一定幾率會(huì)丟失數(shù)據(jù)蔬墩,因此不建議在多進(jìn)程通訊時(shí)采用SharedPreferences。

3.使用Messenger代碼示例

Messenger是輕量級(jí)的IPC方案拇颅,底層實(shí)現(xiàn)是AIDL,他對(duì)AIDL進(jìn)行了封裝樟插,Messenger 服務(wù)端是以串行的方式來(lái)處理客戶端的請(qǐng)求的韵洋,不存在并發(fā)執(zhí)行的情形。
缺憾:無(wú)法并發(fā)黄锤,只適合傳遞消息,無(wú)法跨進(jìn)程調(diào)用方法

4.使用AIDL[代碼示例]
1.AIDL注意

Messenger也是AIDL勉吻,系統(tǒng)做了封裝旅赢。

服務(wù)端首先創(chuàng)建一個(gè)Service用來(lái)監(jiān)聽(tīng)客戶端的連接請(qǐng)求,然后創(chuàng)建一個(gè)AIDL文件短纵,將暴露給客戶端的接口在AIDL文件中聲明,最后在Service中實(shí)現(xiàn)這個(gè)AIDL接口即可香到”ㄆ疲客戶端首先綁定服務(wù)端的Service,綁定成功后充易,將服務(wù)端返回的Binder對(duì)象轉(zhuǎn)化成AIDL接口所屬的類型,調(diào)用相對(duì)應(yīng)的AIDL中的方法盹靴。

注意:

1.AIDL支持的數(shù)據(jù)類型:
     - 基本數(shù)據(jù)類型;
     -  String梭冠、CharSequence改备;
     - List 只支持ArrayList,里面的元素必須都能被AIDL所支持;
     - Map 只支持HashMap盐捷,里面的元素(key和value)必須都能被AIDL所支持;
     -  Parcelable 所有實(shí)現(xiàn)了Parcelable接口的對(duì)象毙驯;
     - AIDL 所有AIDL接口本身也可以在AIDL文件中使用。
2.自定義的Parcelable對(duì)象和AIDL對(duì)象必須顯示的import進(jìn)來(lái)(即使在同一個(gè)包)垦巴。
3.除了基本數(shù)據(jù)類型铭段,需要用inout表示輸入輸出型參數(shù)。
4.為了方便AIDL開(kāi)發(fā)憔披,建議把所有和AIDL相關(guān)的類和文件都放在同一個(gè)包中爸吮,好處在于,當(dāng)客戶端是另一個(gè)應(yīng)用的時(shí)候锰霜,我們可以直接把整個(gè)包復(fù)制到客戶端工程中去桐早。
5.RemoteCallbackList是系統(tǒng)專門(mén)提供用于刪除跨進(jìn)程listener的接口,RemoteCallbackList是泛型友存,支持管理任意的AIDL接口陶衅,因?yàn)樗蠥IDL接口都繼承自android.os.IInterface接口。
6.需注意AIDL客戶端發(fā)起RPC過(guò)程的時(shí)候侠驯,客戶端的線程會(huì)掛起奕巍,如果是UI線程發(fā)起的RPC過(guò)程儒士,如果服務(wù)端處理事件過(guò)長(zhǎng),就會(huì)導(dǎo)致ANR诅福。

2.設(shè)置遠(yuǎn)程服務(wù)權(quán)限

方法1:在onBind中驗(yàn)證

①.在Manifest聲明權(quán)限 
  <permission
        android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE"
        android:protectionLevel="normal" />
②.在Service中的onBind中驗(yàn)證
    @Override
    public IBinder onBind(Intent intent) {
        int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
        Log.d(TAG, "onbind check=" + check);
        if (check == PackageManager.PERMISSION_DENIED) {
            return null;
        }
        return mBinder;
    }
③.在Manifest注冊(cè)權(quán)限
<uses-permission android:name="com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE" />

方法2:在服務(wù)端的onTransact方法中進(jìn)行權(quán)限驗(yàn)證

public boolean onTransact(int code, Parcel data, Parcel reply, int flags)
                throws RemoteException {
            int check = checkCallingOrSelfPermission("com.ryg.chapter_2.permission.ACCESS_BOOK_SERVICE");
            Log.d(TAG, "check=" + check);
            if (check == PackageManager.PERMISSION_DENIED) {
                return false;
            }

            String packageName = null;
            String[] packages = getPackageManager().getPackagesForUid(
                    getCallingUid());
            if (packages != null && packages.length > 0) {
                packageName = packages[0];
            }
            Log.d(TAG, "onTransact: " + packageName);
            if (!packageName.startsWith("com.ryg")) {
                return false;
            }

            return super.onTransact(code, data, reply, flags);
        }
3.AIDl接口實(shí)現(xiàn)觀察者模式

BookManagerService
BookManagerActivity

4.RemoteCallbackList刪除跨進(jìn)程listener

①.使用RemoteCallbackList代替CopyOnWriteArrayList

private RemoteCallbackList<IOnNewBookArrivedListener> mListenerList =  
    new RemoteCallbackList<IOnNewBookArrivedListener>();

②.獲取監(jiān)聽(tīng)器對(duì)象

 final int N = mListenerList.beginBroadcast();
 mListenerList.finishBroadcast();//需要配套使用氓润,哪怕是只獲取一個(gè)
5.注意:

服務(wù)端本身可以做異步操作,客戶端當(dāng)訪問(wèn)耗時(shí)操作時(shí)挨措,需要開(kāi)啟子線程
同理崩溪,當(dāng)服務(wù)端調(diào)用客戶端的listener中的方法時(shí),被調(diào)用的方法也運(yùn)行在Binder線程池中觉既,同樣乳幸,需開(kāi)啟子線程

5.使用ContentProvider

ContentProvider底層實(shí)現(xiàn)也是Binder,專門(mén)用于不同應(yīng)用間的進(jìn)行數(shù)據(jù)共享的方式尝艘,ContentProvider主要是表格的形式來(lái)組織數(shù)據(jù),并且可以包含多個(gè)表眯漩,同時(shí)還支持文件數(shù)據(jù),圖片,視頻等闽颇。

6.使用Socket

網(wǎng)絡(luò)上的兩個(gè)程序通過(guò)一個(gè)雙向的通信連接實(shí)現(xiàn)數(shù)據(jù)的交換兵多,這個(gè)連接的一端稱為一個(gè)socket,

7.使用Binder連接池

當(dāng)我們使用AIDL的使用,有很多業(yè)務(wù)模塊需要用到AIDL剩膘,會(huì)創(chuàng)建很多服務(wù)怠褐,這個(gè)時(shí)候就需要用到BInder連接池,來(lái)管理這些AIDL,這個(gè)時(shí)候就只需要使用一個(gè)Service宪巨,根據(jù)不同的AIDL的code溜畅,去創(chuàng)建不同的AIDL即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末天吓,一起剝皮案震驚了整個(gè)濱河市峦椰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌物邑,老刑警劉巖滔金,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件餐茵,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡忿族,警方通過(guò)查閱死者的電腦和手機(jī)道批,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)椭岩,“玉大人璃赡,你說(shuō)我怎么就攤上這事∷疲” “怎么了豆励?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)技扼。 經(jīng)常有香客問(wèn)我嫩痰,道長(zhǎng),這世上最難降的妖魔是什么丽旅? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任榄笙,我火速辦了婚禮祷蝌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巨朦。我一直安慰自己,他們只是感情好拄查,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布堕扶。 她就那樣靜靜地躺著梭依,像睡著了一般。 火紅的嫁衣襯著肌膚如雪睛挚。 梳的紋絲不亂的頭發(fā)上扎狱,一...
    開(kāi)封第一講書(shū)人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音匠抗,去河邊找鬼污抬。 笑死绳军,一個(gè)胖子當(dāng)著我的面吹牛矢腻,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播奶是,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼竣灌,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼初嘹!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起屯烦,我...
    開(kāi)封第一講書(shū)人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤漫贞,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后芍殖,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體谴蔑,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年窃躲,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蒂窒。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片荞怒。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖衰抑,靈堂內(nèi)的尸體忽然破棺而出荧嵌,到底是詐尸還是另有隱情砾淌,我是刑警寧澤谭网,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布蜻底,位于F島的核電站聘鳞,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏抠璃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一窿春、第九天 我趴在偏房一處隱蔽的房頂上張望旧乞。 院中可真熱鬧磅氨,春花似錦、人聲如沸烦租。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)窃祝。三九已至,卻和暖如春甩栈,著一層夾襖步出監(jiān)牢的瞬間糕再,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工殴蹄, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人刺下。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓稽荧,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親畅卓。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蟋恬,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

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