Android進(jìn)階之IPC機(jī)制(一)

IPC是Inter-Proess Communication的縮寫瑟慈,意思是跨進(jìn)程通信,即兩個進(jìn)程之間進(jìn)行數(shù)據(jù)交換的過程。今天我們就來聊聊Android中的IPC機(jī)制下翎。

IPC基礎(chǔ)

多進(jìn)程

進(jìn)程和線程

線程是CPU調(diào)度的最小單元混稽,是一種有限的系統(tǒng)資源采驻。而進(jìn)程則一般指一個執(zhí)行單元,在PC和移動設(shè)備上指一個程序或應(yīng)用匈勋。

一個進(jìn)程可以包含多個線程礼旅,在Android中,主線程為UI線程洽洁,只有在UI線程中才能操作界面元素痘系。

進(jìn)程和線程之間的關(guān)系其實就相當(dāng)于公司和部門之間的關(guān)系一樣。一個進(jìn)程就相當(dāng)于一個公司饿自,線程就相當(dāng)于公司里的部門汰翠,各司其職,可以并行工作昭雌。最小的公司可能只有一個部門复唤,即最基礎(chǔ)的進(jìn)程只有一個線程。至于多進(jìn)程也很好理解烛卧,有些公司很龐大佛纫,下屬又有好幾個小公司,就像一個龐大的應(yīng)用可能會有多個進(jìn)程一樣总放。

開啟多進(jìn)程模式的方式

常用方法為在AndroidMenifest中給四大組件指定android:process屬性呈宇。(用JNI在native層fork一個新的進(jìn)程也可以實現(xiàn),但不屬于常規(guī)方法)

有兩種效果不同的命名方法:

  1. 省略包名局雄,如android:process=":remote"甥啄,表示進(jìn)程名為com.example.myapplication:remote。屬于當(dāng)前應(yīng)用的私有進(jìn)程炬搭,其他進(jìn)程的組件不能和他跑在同一進(jìn)程中蜈漓。
  2. 完整命名的進(jìn)程,如android:process="com.example.myapplication.remote"尚蝌。屬于全局進(jìn)程迎变,其他應(yīng)用可以通過ShareUID方式和他跑在用一個進(jìn)程中。

關(guān)于UID

Android系統(tǒng)為每個應(yīng)用分配一個唯一的UID飘言,具有相同UID的應(yīng)用才能共享數(shù)據(jù)衣形。

兩個應(yīng)用通過ShareUID跑在同一進(jìn)程的條件:ShareUID相同且簽名也相同。

滿足上述條件的兩個應(yīng)用,無論是否跑在同一進(jìn)程谆吴,它們可共享data目錄倒源,組件信息。

若跑在同一進(jìn)程句狼,它們除了可共享data目錄笋熬、組件信息,還可共享內(nèi)存數(shù)據(jù)腻菇。它們就像是一個應(yīng)用的兩個部分胳螟。

多進(jìn)程產(chǎn)生的問題

  1. 靜態(tài)成員和單例模式失效;
  2. 線程同步機(jī)制失效筹吐;
  3. SharePreferences的可靠性下降糖耸;
  4. Application會重復(fù)創(chuàng)建。

不同的進(jìn)程就是不同的JVM虛擬機(jī)丘薛,那么就會產(chǎn)生問題1和問題2嘉竟。由于SharePreferences底層是基于XML文件的讀寫實現(xiàn)的,那么并發(fā)讀寫肯定很容易出問題,即問題3洋侨。Android系統(tǒng)會為新的進(jìn)程分配獨立虛擬機(jī)舍扰,相當(dāng)于系統(tǒng)又把這個應(yīng)用重新啟動了一次,于是就出現(xiàn)了問題4希坚。

序列化

進(jìn)程中通信會涉及到序列化的相關(guān)內(nèi)容边苹,序列化表示將一個對象轉(zhuǎn)換成可存儲或可傳輸?shù)臓顟B(tài)。序列化后的對象可以在網(wǎng)絡(luò)上進(jìn)行傳輸裁僧,也可以存儲到本地勾给。

兩種類型的變量不會參與序列化:

  1. 靜態(tài)成員變量屬于類,不屬于對象锅知。
  2. 用transient關(guān)鍵字標(biāo)記的成員變量。

Java原生的序列化接口為 Serializable 脓钾,Android獨有的序列化接口為 Parcelable售睹。它們之間的區(qū)別是什么呢?

Serializable的作用是為了保存對象的屬性到本地文件可训、數(shù)據(jù)庫昌妹、網(wǎng)絡(luò)流以方便數(shù)據(jù)傳輸,當(dāng)然這種傳輸可以是程序內(nèi)的也可以是兩個程序間的握截。而Android的Parcelable的設(shè)計初衷是因為Serializable效率過慢飞崖,為了在程序內(nèi)不同組件間以及不同Android程序間(AIDL)高效的傳輸數(shù)據(jù)而設(shè)計,這些數(shù)據(jù)僅在內(nèi)存中存在谨胞,Parcelable是通過IBinder通信的消息的載體固歪。

它們的優(yōu)劣如何?

Parcelable的性能比Serializable好,在內(nèi)存開銷方面較小牢裳,所以在內(nèi)存間數(shù)據(jù)傳輸時推薦使用Parcelable逢防,如activity間傳輸數(shù)據(jù),而Serializable可將數(shù)據(jù)持久化方便保存蒲讯,所以在需要保存或網(wǎng)絡(luò)傳輸數(shù)據(jù)時選擇Serializable忘朝,因為android不同版本Parcelable可能不同,所以不推薦使用Parcelable進(jìn)行數(shù)據(jù)持久化判帮。

Serializable

Serializable是Java提供的序列化接口局嘁,使用起來很簡單,只需要在類的聲明中指定一個serialVersionUID即可(不指定也行),代碼如下:

public class Person implements Serializable {

    private static final long serialVersionUID = 44260788630571004L;
    
    public int id;
    public String name;
    
    ...
}

serialVersionUID是用于在反序列化時進(jìn)行校驗晦墙,相當(dāng)于是一個版號的作用悦昵,如果serialVersionUID不同,說明類可能被改動過了偎痛,沒法反序列化旱捧。

序列化的過程都由系統(tǒng)自動完成了。我們只需使用ObjectOutputStream和ObjectInputStream即可完成序列化和反序列化的操作踩麦。代碼如下:

public static void main(String[] args) {
        Person person = new Person(1,"Jerry");
        try {
            //序列化
            ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream("cache.txt"));
            outputStream.writeObject(person);
            outputStream.close();
            
            //反序列化
            ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream("cache.txt"));
            Person myPerson = (Person) inputStream.readObject();
            inputStream.close();
            
            
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
    }
    

Parcelable

Parcelable接口是Android根據(jù)應(yīng)用場景(多進(jìn)程通信)提供的一種序列化方式枚赡。只要實現(xiàn)這個接口,一個類的對象就可以實現(xiàn)序列化并通過Intent和Binder傳遞谓谦。

相對Serializable贫橙,Parcelable就要復(fù)雜一些,需要實現(xiàn)writeToParcel反粥、describeContents函數(shù)以及靜態(tài)的CREATOR變量卢肃,實際上就是將如何打包和解包的工作自己來定義,而序列化的這些操作完全由底層實現(xiàn)才顿。

public class MyParcelable implements Parcelable {
     private int mData;
     private String mStr;

     public int describeContents() {
         return 0;
     }

     // 寫數(shù)據(jù)進(jìn)行保存
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(mData);
         out.writeString(mStr);
     }

     // 用來創(chuàng)建自定義的Parcelable的對象
     public static final Parcelable.Creator<MyParcelable> CREATOR
             = new Parcelable.Creator<MyParcelable>() {
         public MyParcelable createFromParcel(Parcel in) {
             return new MyParcelable(in);
         }

         public MyParcelable[] newArray(int size) {
             return new MyParcelable[size];
         }
     };

     // 讀數(shù)據(jù)進(jìn)行恢復(fù)
     private MyParcelable(Parcel in) {
         mData = in.readInt();
         mStr = in.readString();
     }
 }

由于Parcelable針對android平臺進(jìn)行過優(yōu)化莫湘,效率要高于Serializable,缺點就是使用起來麻煩一些郑气。

Binder

概念

  1. 從API角度:是一個類幅垮,實現(xiàn)IBinder接口。
  2. 從IPC角度:是Android中的一種跨進(jìn)程通信方式尾组。
  3. 從Framework角度:是ServiceManager連接各種Manager和相應(yīng)ManagerService的橋梁忙芒。
  4. 從應(yīng)用層:是客戶端和服務(wù)端進(jìn)行通信的媒介』淝龋客戶端通過它可獲取服務(wù)端提供的服務(wù)或者數(shù)據(jù)呵萨。

Android是基于Linux內(nèi)核基礎(chǔ)上設(shè)計的,大家都知道跨跨,Linux內(nèi)核有多種進(jìn)程間通信的方式:管道/消息隊列/共享內(nèi)存/Socket等潮峦,為啥還要搞個Binder 出來呢?

性能方面:

Binder相對于傳統(tǒng)的Socket方式,更加高效跑杭。Binder數(shù)據(jù)拷貝只需要一次铆帽,而管道、消息隊列德谅、Socket都需要2次爹橱,共享內(nèi)存方式一次內(nèi)存拷貝都不需要,但實現(xiàn)方式又比較復(fù)雜窄做。

安全方面:

Binder機(jī)制從協(xié)議本身就支持對通信雙方做身份校檢愧驱,從而大大提升了安全性。

一些理解

感覺網(wǎng)上好多大佬都講過Binder機(jī)制椭盏,長篇大論组砚,內(nèi)容不可謂不細(xì)致,不過對于新手而言有點難以理解掏颊,我一開始看的時候就感覺云里霧里的糟红。由于細(xì)致的講解網(wǎng)上到處都是,我這種菜鳥也不可能講的更好乌叶,所以下面我還是來談?wù)勎掖譁\的認(rèn)識吧盆偿。

首先是Binder機(jī)制的四個組件

Client、Server准浴、ServiceManager以及Binder驅(qū)動事扭。

Binder通信采用C/S架構(gòu),那么一開始的問題就是哪個是客戶端哪個是服務(wù)端呢乐横?

答案是不一定求橄。乍一看,Client葡公、Server一個客戶端一個服務(wù)端罐农,似乎挺合理的,但其實在Binder機(jī)制中Server有可能也是客戶端催什,而服務(wù)端是ServiceManager啃匿。

至于Binder驅(qū)動,則可以看成是一個連接客戶端和服務(wù)端的橋梁蛆楞,相當(dāng)于一個虛擬的硬件設(shè)備。

進(jìn)程間通信其實就是交互數(shù)據(jù)對吧夹厌,那么Client就是要獲取數(shù)據(jù)的進(jìn)程豹爹,Server就是提供數(shù)據(jù)的進(jìn)程。

我的理解是:Binder 機(jī)制就是 Client 和 Server 在 ServiceManager 進(jìn)程的管理下矛纹,通過Binder驅(qū)動進(jìn)行通信臂聋。

先從ServiceManager說起

ServiceManager 是系統(tǒng)級的服務(wù),單獨運行在一個進(jìn)程中,從字面意思來看孩等,它是用來管理 Service 的艾君。Service 就是 Server 提供的服務(wù)。那么它是怎么管理的呢肄方?

首先冰垄,假設(shè)如果讓我們自己管理一堆服務(wù)我們會怎么辦呢?

直接用服務(wù)的名字么权她?大部分人應(yīng)該會選擇給每個服務(wù)分配一個獨一無二的ID吧虹茶,其實 ServiceManager 也是這么做的。

只不過ServiceManager用的是句柄隅要。句柄是啥蝴罪?英文Handle,簡單點說就是一個整數(shù)值步清,可以看作是 service 的ID要门。

ServiceManager錄了所有系統(tǒng)service所對應(yīng)的Binder句柄,它的核心功能就是維護(hù)好這些句柄值廓啊。因此對于Server來說欢搜,在ServiceManager中注冊就是創(chuàng)建自己的句柄值,而Client查詢也是查詢目標(biāo)服務(wù)的句柄值崖瞭。通過句柄值狂巢,Client 就可以獲取對應(yīng)的 Service 的Binder 代理,從而完成通信书聚。

那么唧领,之前提到 ServiceManager 也是一個單獨的進(jìn)程,那么其他進(jìn)程是如何獲取它的句柄值的呢雌续?

ServiceManager 本身的句柄值固定為0斩个,即handle=0。因此驯杜,無論是Client受啥,還是 Server 都可以通過這個固定值直接獲得 ServiceManager 的Binder代理,從而和ServiceManager進(jìn)行通信鸽心。

另外滚局,句柄值都是ServiceManager 維護(hù)的,不同進(jìn)程中指代相同Binder實體的句柄值可能是不同的顽频,即A進(jìn)程拿到的句柄值和B進(jìn)程拿到的句柄值可能不同藤肢,但指代的是同一個 Serivce。

Binder 機(jī)制的流程

  1. Android系統(tǒng)啟動時會啟動糯景,ServiceManager進(jìn)程嘁圈。ServiceManager 注冊自己的handle值為0省骂。
  2. ServiceManager在死循環(huán)中,不停地去讀內(nèi)核中binder driver最住,看是否有新的請求(注冊Service或查詢Service)
  3. Server 創(chuàng)建一個handle為0的代理binder钞澳,向ServiceManager注冊自己。
  4. Server 進(jìn)入無限循環(huán)涨缚,不停地去讀內(nèi)核中binder driver的請求數(shù)據(jù)轧粟,如果是發(fā)送給自己的,解包Parcel對象仗岖,處理并將結(jié)果返回逃延。
  5. Client 創(chuàng)建一個handle為0的代理binder,向ServiceManager 查詢Service 的handle值轧拄。
  6. Client 通過請求到的 handle值揽祥,獲得該service 的代理binder ,通過代理binder調(diào)用service的方法檩电。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末拄丰,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子俐末,更是在濱河造成了極大的恐慌料按,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件卓箫,死亡現(xiàn)場離奇詭異载矿,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)烹卒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門闷盔,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人旅急,你說我怎么就攤上這事逢勾。” “怎么了藐吮?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵溺拱,是天一觀的道長。 經(jīng)常有香客問我谣辞,道長迫摔,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任泥从,我火速辦了婚禮句占,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘歉闰。我一直安慰自己辖众,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布和敬。 她就那樣靜靜地躺著凹炸,像睡著了一般。 火紅的嫁衣襯著肌膚如雪昼弟。 梳的紋絲不亂的頭發(fā)上啤它,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機(jī)與錄音舱痘,去河邊找鬼变骡。 笑死,一個胖子當(dāng)著我的面吹牛芭逝,可吹牛的內(nèi)容都是我干的塌碌。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼旬盯,長吁一口氣:“原來是場噩夢啊……” “哼台妆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起胖翰,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤接剩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后萨咳,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體懊缺,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年培他,在試婚紗的時候發(fā)現(xiàn)自己被綠了鹃两。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡靶壮,死狀恐怖怔毛,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腾降,我是刑警寧澤拣度,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站螃壤,受9級特大地震影響抗果,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜奸晴,卻給世界環(huán)境...
    茶點故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一冤馏、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧寄啼,春花似錦逮光、人聲如沸代箭。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽嗡综。三九已至,卻和暖如春杜漠,著一層夾襖步出監(jiān)牢的瞬間极景,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工驾茴, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留盼樟,地道東北人。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓锈至,卻偏偏與公主長得像晨缴,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子裹赴,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,055評論 2 355

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