IPC-進程間通訊

開啓多進程

進程名以":"開頭的講程屬於當前應用的私有進程,其餘應用的組件不能夠和它跑在同一個進程中,而進程名不以開頭的進程屬於全局進程, 其餘應用經過ShareUID方式能夠和它跑在同一個進程中。緩存

咱們知道Andrid系統會爲每一個應用分配一個惟一的UID,具備相同UID的應用才能共享數據。這裏要說明的是,兩個應用經過ShareUID跑在同一個進程中是有要求的,須要這兩個應用有相同的ShareUID而且簽名相同才能夠。在這種狀況下,它們能夠互相訪問對方的私有數據,好比data目錄、組件信息等,無論它們是否跑在同一個進程中。 固然若是它們跑在同一個進程中,那麼除了能共享data目錄、組件信息,還能夠共享內存數據,或者說它們看起來就像是一個應用 的兩個部分。bash

多進程形成問題

  • 靜態成員變量和單例對象徹底失效
  • 線程同步失效
  • sp可靠性降低
  • Application屢次建立

IPC方式

  • 使用Bundle
  • 共享文件(sp不可靠,系統對sp的讀寫作了緩存策略)
  • Binder
  • ContentProvider,天生支持跨進程
  • 網絡通訊實現數據傳遞,socket
  • Messenger,底層由AIDL實現,一次處理一個請求(Handler)

Messenger

Messenger能夠翻譯爲信使,顧名思義,經過它能夠在不一樣進程中傳遞Mesge對象, 在Message中放入咱們須要傳遞的數據,就能夠輕鬆地實現數據的進程間傳遞了網絡

  • 服務端進程socket

    首先,咱們須要在服務端建立一個Service來處理客戶端的鏈接請求,同時建立一個Handler並經過它來建立個Messenger對象,而後在Service的onBind中返回這個Messenger對象底層的Binder便可。ide

  • 客戶端進程ui

    客戶端進程中,首先要綁定服務端的Service, 綁定成功後用服務端返回的IBinder 對 象建立一個Messenger,經過這個Messenger 就能夠向服務端發送消息了,發消息類型爲 Message 對象。若是須要服務端可以迴應客戶端,就和服務端一*樣,咱們還須要建立 一個 Handler並建立一個新的Messenger, 並把這個Messenger對象經過Message的replyTo參數 傳遞給服務端,服務端經過這個replyTo參數就能夠迴應客戶端。spa

Binder

直觀來講,Binder 是Android中的一個類, 它實現了IBinder 接口。從IPC 角度來講, Binder是Android中的一種跨進 程通訊方式,Binder 還能夠理解爲一種虛擬的物理設備,它的設備驅動是/dev/binder,該通訊方式在Linux中沒有;從Android Framework角度來講Binder是ServiceManager鏈接各類Manager (ActivityManager、WindowManager,等等)相應ManagerService的橋樑;線程

從Android 應用層來講,Binder 是客戶端和服務端進行通化的媒介,當bindService 的時候,服務端會返回一一個包含了服務端業務調用的Binder對象經過這個Binder對象,客戶端就能夠獲取服務端提供的服務或者數據,這裏的服務包括通服務和基於AIDL的服務。翻譯

Android開發中,Binder主要用在Service中,包括AIDL和Messenger,其中普通Serv中的Binder不涉及進程間通訊,因此較爲簡單,沒法觸及Binder的核心,而Messenget底層實際上是AIDL。代理

AIDL支持的數據類型

  • 基本數據類型
  • String和CharSequence
  • List,只支持ArrayList,裏面每一個元素都必須可以被AIDL支持
  • Map,只支持HashMap,裏面每一個元素都必須可以被AIDL支持
  • Parcelable,全部實現了Parcelable接口的對象
  • AIDL,全部的aidl接口自己也能夠在adil文件中使用
aidl注意
  • aidl文件中使用了自定義的Parcelable對象和aidl對象必須顯式import進來,同一個包路徑下也要導入

  • aidl文件中使用了自定義的Parcelable對象,那必須建立一個和自定義的Parcelable對象同名的aidl文件,而且在使用到這個對象的aidl文件中聲名它爲Parcelable類型

    parcelable User;
    複製代碼
  • aidl文件中每一個實現Parcelable接口的類都須要聲名parcelable,以外除了基本數據類型,其餘類型參數必須標上方向:in out inout

    • in:輸入型參數
    • out:輸出型參數
    • inout:輸入輸出參數
    interface IBookManager{
        void addBook(in Book book);
    }
    複製代碼
  • aidl接口只支持方法,不支持聲明靜態常量

  • aidl的包結構在服務端和客戶端要保持一直.由於客戶端須要反序列化服務端中和aidl接口相關的全部類,類的完整路徑不同反序列化失敗

  • aidl方法是在服務端的Binder線程池中執行,CopyOnWriteArrayList自動線程同步,還有ConcurrentHashMap

  • RemoteCallbackList,自動線程同步,進程終止時自動移除註冊的listener;beginBroadcast()和finishBroadcast()必須配對使用

  • 服務端aidl接口方法所有運行在Binder線程池中,接口方法能夠作耗時操做,若是調用接口的客戶端是在UI線程中,那麼不要再接口方法中作耗時操做不然就會致使ANR;若是明確知道必須作耗時操做,那麼在調用接口的客戶端中須要開啓線程來調用耗時的aidl接口方法

  • 客戶端中的aidl接口運行在客戶端的線程池中,不能作刷新UI;

  • 服務端在UI線程中(好比在onBind方法中獲取客戶端的相關信息)調用客戶端耗時的aidl接口,會致使服務端ANR

  • 客戶端的onServiceConnected()和onServiceDisconnected()運行在UI線程

Binder死亡、Binder鏈接斷裂
  • 兩種方法收到Binder死亡通知
    • 設置DeathRecipient監聽-->binderDied()
    • ServiceConnection-->onServiceDisconnected()方法
    • 兩者區別
    • onServiceDisconnected()在客戶端的UI線程中被回調,binderDied()在客戶端的Binder線程池中被回調,binderDied()不能訪問UI
  • Binder提供兩個配對使用方法:linkToDeath()和unlinkToDeath(),經過linkToDeath方法設置一個死亡代理,Binder死亡時收到通知,此時能夠從新發起請求從新鏈接
private IBinder.DeathRecipient recipient = new IBinder.DeathRecipient(){
    
    @override
    public void binderDied(){
        if(iBookManager == null){
            return;
        }
        iBookManager.asBinder().unlinkToDeath(recipient,0);
        iBookManager = null;
        //這裏從新綁定遠程Service
    }
};
複製代碼

其次,在客戶端綁定遠程服務成功以後,給Binder設置死亡代理

@Override
public void onServiceConnected(ComponentName name, IBinder binder) {
    iBookManager =  IBookManager.Stub.asInterface(binder);
    binder.linkToDeath(recipient,0);
}
複製代碼
aidl權限驗證
  • 在onBind()中經過自定義權限驗證,沒有權限直接返回null
  • 經過UID和PID驗證,使用getCallingUid()和getCallingPid()能夠拿到客戶端所屬應用的UID和PID
  • 經過UID和PID這兩個參數拿到包名進行包名驗證
ContentProvider
//authority與表名關聯,根據傳入的URI取出來外界想要操做哪張表
//content://com.icbc.im.provider/user
//content://com.icbc.im.provider/book
UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
uriMatcher.addURI(authority,  path, code);
uriMatcher.addURI(authority,  path, code);
複製代碼
  • onCreate()主線程
  • insert query update delete Binder線程池中
  • 數據發生改變通知外界
getContext().getContentResolver().notifyChange(uri, null);
複製代碼

外界註冊數據改變監聽

getContext().getContentResolver().registerContentObserver(uri,true,new ContentObserver(null) {
            @Override
            public void onChange(boolean selfChange, Uri uri) {
                super.onChange(selfChange, uri);
            }
        });
//解除註冊
getContext().getContentResolver().unregisterContentObserver()複製代碼
相關文章
相關標籤/搜索