Android進程間通訊詳解

因爲android系統中應用程序之間不能共享內存。所以,在不一樣應用程序之間交互數據(跨進程通信)就稍微麻煩一些。

進程間通訊(ipc)html

IPC方法老是產生客戶/服務端模式的調用,也便是客戶端組件(Activity/Service)持有服務端Service的組件,只能是客戶端主動調用服務端的方法,服務端沒法反過來調用客戶端的方法,由於IPC的另外一端Service沒法獲取客戶端的對象。linux

binderandroid

Binder 是一種進程間通訊機制。安卓中跨進程通信就是經過binder。當綁定服務的時候會返回一個binder對象,而後經過他進行多進程間的通訊。Binder只須要一次數據拷貝,性能上僅次於共享內存。多線程

在 Android 系統中,這個運行在內核空間,負責各個用戶進程經過 Binder 實現通訊的內核模塊就叫 Binder 驅動(Binder Dirver)。併發

Binder IPC 機制中涉及到的內存映射經過 mmap() 來實現,mmap() 是操做系統中一種內存映射的方法。內存映射簡單的講就是將用戶空間的一塊內存區域映射到內核空間。映射關係創建後,用戶對這塊內存區域的修改能夠直接反應到內核空間;反以內核空間對這段區域的修改也能直接反應到用戶空間。app

內存映射能減小數據拷貝次數,實現用戶空間和內核空間的高效互動。兩個空間各自的修改能直接反映在映射的內存區域,從而被對方空間及時感知。也正由於如此,內存映射可以提供對進程間通訊的支持。ide

Binder IPC 正是基於內存映射(mmap)來實現的
Android進程間通訊詳解Android進程間通訊詳解
Binder 通訊中的代理模式
咱們已經解釋清楚 Client、Server 藉助 Binder 驅動完成跨進程通訊的實現機制了,可是還有個問題會讓咱們困惑。A 進程想要 B 進程中某個對象(object)是如何實現的呢?畢竟它們分屬不一樣的進程,A 進程 無法直接使用 B 進程中的 object。函數

前面咱們介紹過跨進程通訊的過程都有 Binder 驅動的參與,所以在數據流經 Binder 驅動的時候驅動會對數據作一層轉換。當 A 進程想要獲取 B 進程中的 object 時,驅動並不會真的把 object 返回給 A,而是返回了一個跟 object 看起來如出一轍的代理對象 objectProxy,這個 objectProxy 具備和 object 一摸同樣的方法,可是這些方法並無 B 進程中 object 對象那些方法的能力,這些方法只須要把把請求參數交給驅動便可。對於 A 進程來講和直接調用 object 中的方法是同樣的。性能

當 Binder 驅動接收到 A 進程的消息後,發現這是個 objectProxy 就去查詢本身維護的表單,一查發現這是 B 進程 object 的代理對象。因而就會去通知 B 進程調用 object 的方法,並要求 B 進程把返回結果發給本身。當驅動拿到 B 進程的返回結果後就會轉發給 A 進程,一次通訊就完成了
Android進程間通訊詳解Android進程間通訊詳解
其實進程間通訊就是爲了實現數據共享。一個程序不一樣組件在不一樣進程也叫多進程,和倆個應用沒有本質區別。使用process屬性能夠實現多進程,可是會帶來不少麻煩,主要緣由是共享數據會失敗,弊端有:靜態和單利失效,同步失效,sharedprefer也變的不可靠等問題。操作系統

多進程通訊的方式

1.使用intent的附加信息extras來傳遞,經過bundle,傳遞的是bundle支持的類型,好比基本數據類型、實現pracellable或serializeable的對象

/**指定包名和帶包名的Activity的名字*/
ComponentName componentName = new ComponentName("com.example.androidaidl", "com.example.androidaidl.MainActivity");
Intent intent = new Intent();
intent.putExtra("id", 1001);
intent.setComponent(componentName);
startActivity(intent);

2.使用文件共享,序列化或是sharedpre,不過不適用於讀寫併發的操做
3.廣播:Android的廣播是系統級的,只要傳遞的Action同樣,就能夠接收到其餘進程廣播的消息,廣播中能夠經過Intent傳遞數據。
4.scheme協議是android中的一種頁面內跳轉協議,經過定義本身的scheme協議,能夠很是方便跳轉app中的各個頁面,而且傳遞數據,仍是能夠經過H5頁面跳轉指定頁面等。
5.ContentProvider(進程間數據共享)和message同樣,底層也是binder,除了oncreate方法其餘方法(crud)都是運行在bindler線程裏。因此在oncerate裏不能作耗時操做。Android自己就提供了很多的ContentProvider訪問,好比聯繫人、相冊等。 訪問ContentProvider,須要經過Uri,須要以「content://」開頭。在其餘應用訪問經過uri(主機名):

ContentResolver resolver = getActivity().getContentResolver();
/**com.mh.getdata/stock這個要和Provider所在進程中添加的Uri一致*/
Uri uri = Uri.parse("content://com.mh.getdata/stock");
Cursor cursor = resolver.query(uri, null, null, null, null);

常規通信

只有容許不一樣應用的客戶端用 IPC 方式訪問服務,而且想要在服務中處理多線程(多任務)時,纔有必要使用 AIDL。 若是您不須要執行跨越不一樣應用的併發 IPC,就應該經過實現一個 Binder 建立接口;或者,若是您想執行 IPC,但根本不須要處理多線程,則使用 Messenger 類來實現接口。不管如何,在實現 AIDL 以前,請您務必理解綁定服務。
aidl文檔

1.經過 Messenger進行傳遞(handler),在遠程服務裏建立handler(接收客戶端發送的消息)、 Messenger對像,在onbind裏返回( Messenger.getbinder)。在客戶端綁定服務,拿着 Messenger對象發消息(能夠用bundle)。在遠程服務的handlermessage方法就會收到。他是一個個處理的,若是大量併發請求用aidl, Messenger底層就是aidl

在客戶端中建立一個Messenger。而後,當客戶端收到 onServiceConnected() 回調時,會向服務發送一條 Message,並在其 send() 方法的 replyTo 參數中包含客戶端的 Messenger。
注意:Messenger和Message是倆個東西

public void sayHello(View v) {

        if (!mBound) return;
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
           mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

2.直接使用Binder對象:缺點是這種方式不能進行跨進程,跨應用程序的函數調用。只能實如今同一個進程之中,同一個應用程序之中的不一樣的組件之間通信。

用法:繼承Binder,而後在service裏return
繼承Binder用它的對象返回,客戶端將bind對象強轉成自定義Bind

AIDL

Android interface definition language (android接口定義語言) , 用來跨進程的訪問方法。

aidl操做步驟:
1.在兩個項目中新建普通文件(new ->General->File),後綴名改爲(aidl),客戶端和服務端中這個文件所在的包名要保持一致,內容也要同樣
編譯以後, 會在gen目錄下,自動產生同名的,後綴爲 Java 的文件。裏面有咱們要用到的 Stub類。

public static abstract class Stub extends android.os.Binder implements com.example.aidl.AidlFunctions

2.在接口文件AIDLFunctions.aidl中,咱們定義一個方法 show

interface AidlFunctions{
    void show();
}

3.AIDL的使用,須要一個Service配合,因此咱們在服務端還要聲明一個Service

public class AIDLService extends Service {
//stub就是系統自動產生的
    AidlFunctions.Stub binder;

    @Override
    public void onCreate() {
        // TODO Auto-generated method stub
        super.onCreate();
    }

    @Override
    public IBinder onBind(Intent intent) {
        // TODO Auto-generated method stub
        binder = new AidlFunctions.Stub() {

            @Override
            //這裏是咱們在接口中聲明的方法的實現
            public void show() throws RemoteException {
                // TODO Auto-generated method stub
                System.out.println("--------------------收到----------------------");
            }
        };
        return binder;
    }   
}

4.客戶端:

//綁定服務,要用到ServiceConnection 
private ServiceConnection serviceConnection;
//自定義的接口,和服務端同樣
private AidlFunctions aidlFunctions;

serviceConnection = new ServiceConnection() {

    @Override
    public void onServiceDisconnected(ComponentName name) {
        System.out.println("--------------------ServiceDisconnected----------------------");
    }

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        System.out.println("--------------------ServiceConnected----------------------");
        aidlFunctions = AidlFunctions.Stub.asInterface(service);
    }
};
Intent intent = new Intent("com.example.androidaidl.AIDLService");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
//調用show方法
try {
    aidlFunctions.show();
} catch (RemoteException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

使用多進程顯而易見的好處就是分擔主進程的內存壓力。咱們的應用越作越大,內存愈來愈多,將一些獨立的組件放到不一樣的進程,它就不佔用主進程的內存空間了。固然還有其餘好處,有些應用後臺是有多個進程的,啓動一個不可見的輕量級私有進程,在後臺收發消息,或者作一些耗時的事情,或者開機啓動這個進程,而後作監聽等。還有就是防止主進程被殺守護進程,守護進程和主進程之間相互監視,有一方被殺就從新啓動它。由於它們要常駐後臺,特別是即時通信或者社交應用。

本文地址:https://www.linuxprobe.com/android-strude-mulit.html

相關文章
相關標籤/搜索