IPC機制

Android IPC簡介

(1)IPC是Inter-Process Communication的縮寫,含義爲進程間通訊或者跨進程通訊,是指兩個進程之間進行數據交換的過程。java

(2)ANR是Application Not Responding的縮寫,即應用無響應。主線程執行大量的耗時操做容易致使ANR現象發生。android

(3)在Android中最有特點的進程間通訊方式就是Binder了,經過Binder能夠輕鬆地實現進程間通訊。git

(4)Android還支持Socket,經過Socket也能夠實現任意兩個終端或者兩個進程之間的通訊。github

什麼是進程,什麼是線程,進程和線程是兩個大相徑庭的概念。數組

    在操做系統中,線程是CPU調度的最小單元,同時線程是一種有限的系統資源。而進程指的一個執行單元,在PC和移動設備上指的是一個程序或者一個應用。一個進程能夠包含多個線程,所以進程和線程是包含被包含的關係,最簡單狀況下,一個進程能夠只有一個線程,即主線程,在Android裏面也叫UI線程,在UI線程裏才能操做界面元素。服務器

 

    Android中通常都是經過Binder實現進程間通訊。除了Binder,Android還支持Socket,經過Socket也能夠實現任意兩個終端之間的通訊,固然一個設備上的兩個進程之間經過Socket通訊天然也是能夠的。網絡

 

多進程模式的運行機制

 

(1)多進程會帶來不少意想不到的麻煩,由於Android爲每個應用都分配了一個獨立的虛擬機,或者說爲每一個進程都分配了一個獨立的虛擬機,不一樣的虛擬機在內存分配上有不一樣的地址空間,這就致使在不一樣的虛擬機中訪問同一個類的對象會產生多份副本。這樣很就容易致使數據不一樣步。併發

(2)全部運行在不一樣進程的四大組件,只要它們之間須要經過內存在共享數據,都會共享失敗。app

(3)主要有如下四方面的問題:ide

    1)靜態成員和單例模式徹底失效。(由獨立虛擬機形成)

    2)線程同步機制徹底失效。(同上)

    3)SharedPreferences的可靠性降低。(存在併發讀寫的問題)

    4)Application會屢次建立。(新的進程中又會致使進程所在的Application在新的虛擬機中再次建立)

(4)運行在同一個進程中的組件是屬於同一個虛擬機和同一個Application的,同理運行在不一樣進程的組件是屬於兩個不一樣的虛擬機和Application的。

 

基於上面的這些問題,由於咱們須要學習進程間通訊機制!!!!!

 

IPC基礎概念介紹

    當咱們須要經過Intent和Binder傳輸數據時就須要使用Parcelable或者Serializeble。Serializable和Parcelable接口能夠完成對象的序列化過程。還有時候咱們須要把對象持久化到存儲 設備上或者經過網絡傳輸給其餘客戶端,這個時候也須要Serializable來完成對象的持久化。

 

 

Parcelable方法:    

@Override
public Test createFromParcel(Parcel in) {
    return new Test(in);
}

從序列化後的對象中建立原始對象

@Override
public Test[] newArray(int size) {
    return new Test[size];
}

建立指定長度的原始對象數組

protected Test/*Test爲類名*/(Parcel in) {

}

將序列化後的對象中建立原始對象

@Override
public void writeToParcel(Parcel dest, int flags) {
}

將當前對象 寫入序列化結構中,其中flags標識有倆種值,0或者1,爲1時標識當前對象須要做爲返回值返回,不能當即釋放資源,通常都會返回0

writeToParcel()的標記位爲:

PARCELABLE_WRITE_RETURN_VALUE
@Override
public int describeContents() {
    return 0;
}

返回當前對象的內容描述,若是含有文件描述符,返回1,不然返回0,通常都返回0

describeContents()的標記位爲:

CONTENTS_FILE_DESCRIPTOR

 

 

 

Serializable和Parcelable的區別:

        Serializable是java中的序列化接口,使用簡單,開銷大,由於都是使用IO流進行操做

        Parcelable是Android中的序列化方式,所以更適合用在Android平臺上,缺點就是他使用起來比較麻煩,可是他的效率比較高,Parcelable主要運用在內存序列化上,經過Parcelable將對象序列化到存儲設備中,或者將對象序列化後經過王闊傳輸也能夠,可是過程比較複雜

 

 

Binder:

        Binder是Android中的一個類,它實現了IBinder接口,是跨進程通訊的一種方式

     從Framework角度來講,Binder是ServiceManager鏈接各類Manager和相應ManagerService的橋樑

        從Android  應用層來講,Binder是客戶端和服務器端進行通訊的媒介,當bindService時,客戶端能夠獲取服務器提供的服務或者數據,服務包括普通服務和基於ALDL的服務

 

 

AIDL

AIDL有倆個類:Stub,Proxy

類中方法含義:

1)DESCRIPTOR:

      Binder的惟一標識,通常用當前Binder的類名錶示

2)asInterface()

      用於將服務器端的Binder對象轉換成客戶端所需的AIDL接口類型的對象,轉換是區分進程的,若是客戶端和服務器端位於同一進程,那麼此方法返回的就是服務器端的Stub對象自己,不然返回的是系統封裝後的Stub.proxy對象

3)asBinder()

      此方法返回當前Binder對象

4)onTransact()

      此方法運行在服務器端中的Binder線程池,當客戶端發起跨進程請求時,遠程請求會經過系統底層封裝後交由此方法來處理

      服務器經過code能夠肯定客戶端所請求的目標方法是什麼,接着從data中取出目標方法所需的參數,而後執行目標方法。當目標方法執行完畢後,就像reply中寫入返回值

      若是此方法返回false,那麼客戶端的請求會失敗,所以咱們能夠利用這個特性來作權限驗證

   

 

 

Binder工做機制:

Binder中的方法:

        linkToDeath和unlinkToDeath

       Binder運行在服務端進程,若是服務端進程因爲某種緣由異常終止,服務器端的Binder鏈接斷裂,會致使遠程調用失敗,若是說你並不知道鏈接已經斷裂,那麼客戶端的功能會收到影響,於是會有這倆個方法,當Binder死亡時,咱們額能夠收到通知,從而從新發起鏈接請求而恢復鏈接。

 

使用這倆個方法

IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {
    @Override
    public void binderDied() {
        //此處填寫
    }
};

 

 

IPC實現方式

1.使用Bundle

2.使用文件共享

        使用文件共享時要儘可能避免併發讀寫的狀況,能夠考慮使用線程同步來解決這種問題,因此文件共享的方式只適合在對數據同步要求不高的進程之淺見進行通訊。

3.使用Messenger

        簡單實現 Messenger:

            須要有服務端跟客戶端

            1.服務端進程:

                建立一個Service來處理客戶端的鏈接請求,同時建立一個Handler並經過它來建立一個Messenger對象,而後再Service的onBind中返回這個Messenger對象底層的Binder便可。

package com.example.messenger;

import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;

public class MessengerService extends Service {

    private static class MessengerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 100:

                    Log.e("TAGS","客戶端發送的消息:"+msg.getData().getString("key"));
                    Messenger replyTo = msg.replyTo;
                    Message message = Message.obtain(null,200);
                    Bundle bundle = new Bundle();
                    bundle.putString("replyTo","服務器端已經收到了您發送的消息。");
                    message.setData(bundle);
                    try {
                        replyTo.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }

                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private final Messenger messenger = new Messenger(new MessengerHandler());

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
}

!!!須要註冊service

<service
    android:name=".MessengerService"
    android:enabled="true"
    android:exported="true"></service>

            2.客戶端進程:

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

package com.example.messenger;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    private Messenger mService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = new Messenger(iBinder);
            Message message = Message.obtain(null, 100);
            Bundle bundle = new Bundle();
            bundle.putString("key", "Hello Word!");
            message.setData(bundle);
            //當客戶端發送消息的時候,須要把接受服務器回覆的Messenger經過Message的replyTo參數傳遞給服務器
            message.replyTo = messenger;
            try {
                mService.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

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

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

    private Messenger messenger = new Messenger(new MessengerHandler());

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 200:

                    getData(msg);

                    break;
                default:
                    super.handleMessage(msg);

            }
        }

        private void getData(Message msg) {
            Bundle data = msg.getData();
            String replyTo = data.getString("replyTo");
            Log.e("TAGS","服務器返回的消息:"+replyTo);
        }
    }

}

        Messenger的工做原理:

        Messenger是以串行的方式處理客戶端發來的消息,若是發送不少消息到服務端,可是服務端只能一個個去處理,因此遇到這種狀況就不適合使用Messenger了,並且Messenger不支持跨進程調用服務端的方法。

Demo地址:

https://github.com/HaoMoster/Messenger

4.使用AIDL

AIDL簡介:

    Android Interface Definition Language,即Android接口定義語言;用於讓某個Service與多個應用程序組件之間進行跨進程通訊,從而能夠實現多個應用程序共享同一個Service的功能。

  • 在多進程通訊中,存在兩個進程角色(以最簡單的爲例):服務器端和客戶端

服務器端(Service):

步驟一:新建定義AIDL文件,並聲明該服務須要向客戶端提供的接口

interface AIDL_Service1 {
    void AIDL_Service();

}
//AIDL中支持如下的數據類型
//1. 基本數據類型
//2. String 和CharSequence
//3. List 和 Map ,List和Map 對象的元素必須是AIDL支持的數據類型;
//4. AIDL自動生成的接口(須要導入-import)
//5. 實現android.os.Parcelable 接口的類(須要導入-import)

步驟2:在Service子類中實現AIDL中定義的接口方法,並定義生命週期的方法(onCreat、onBind()、blabla)

public class MyService extends Service {

    // 實例化AIDL的Stub類(Binder的子類)
    AIDL_Service1.Stub mBinder = new AIDL_Service1.Stub() {

        //重寫接口裏定義的方法
        @Override
        public void AIDL_Service() throws RemoteException {
            System.out.println("客戶端經過AIDL與遠程後臺成功通訊");
        }
    };


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

        System.out.println("執行了onCreat()");

    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        System.out.println("執行了onStartCommand()");
        return super.onStartCommand(intent, flags, startId);


    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        System.out.println("執行了onDestory()");
    }

    //在onBind()返回Stub類實例
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {

        System.out.println("執行了onBind()");

        return mBinder;
    }




    @Override
    public boolean onUnbind(Intent intent) {
        System.out.println("執行了onUnbind()");
        return super.onUnbind(intent);
    }

}

步驟3:在AndroidMainfest.xml中註冊服務 & 聲明爲遠程服務

<service android:name=".MyService" 
    android:process=":remote"
    android:exported="true">    
</service>

 

客戶端(Client):

步驟1:拷貝服務端的AIDL文件到目錄下
步驟2:使用Stub.asInterface接口獲取服務器的Binder,根據須要調用服務提供的接口方法
步驟3:經過Intent指定服務端的服務名稱和所在包,綁定遠程Service

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="scut.carson_ho.service_client.MainActivity">


    <Button
        android:layout_centerInParent="true"
        android:id="@+id/bind_service"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="綁定服務"
        />
</RelativeLayout>

 

package scut.carson_ho.service_client;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;

import scut.carson_ho.service_server.AIDL_Service1;


public class MainActivity extends AppCompatActivity {

        private Button bindService;

        //定義aidl接口變量
        private AIDL_Service1 mAIDL_Service;

        //建立ServiceConnection的匿名類
        private ServiceConnection connection = new ServiceConnection() {

            //重寫onServiceConnected()方法和onServiceDisconnected()方法
            //在Activity與Service創建關聯和解除關聯的時候調用
            @Override
            public void onServiceDisconnected(ComponentName name) {
            }

            //在Activity與Service創建關聯時調用
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {

                //使用AIDLService1.Stub.asInterface()方法將傳入的IBinder對象傳換成了mAIDL_Service對象
                mAIDL_Service = AIDL_Service1.Stub.asInterface(service);

                try {

                    //經過該對象調用在MyAIDLService.aidl文件中定義的接口方法,從而實現跨進程通訊
                    mAIDL_Service.AIDL_Service();

                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        };


        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);

            bindService = (Button) findViewById(R.id.bind_service);

            //設置綁定服務的按鈕
            bindService.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {

                    System.out.println("點擊了[綁定服務]按鈕");

                    //經過Intent指定服務端的服務名稱和所在包,與遠程Service進行綁定
                    //參數與服務器端的action要一致,即"服務器包名.aidl接口文件名"
                    Intent intent = new Intent("scut.carson_ho.service_server.AIDL_Service1");

                    //Android5.0後沒法只經過隱式Intent綁定遠程Service
                    //須要經過setPackage()方法指定包名
                    intent.setPackage("scut.carson_ho.service_server");

                    //綁定服務,傳入intent和ServiceConnection對象
                    bindService(intent, connection, Context.BIND_AUTO_CREATE);

                }
            });
        }

    }

 

5.使用ContentProvider

        ContentProvider是Android中提供的專門用於不一樣應用間進行數據共享的方式

  • 建立類繼承 ContentProvider重寫onCreate、query、update、insert、delete、getType(用來返回一個Uri請求所對應的MIME類型【媒體類型】,若是不須要關注類型,返回null就能夠)
  • 註冊 ContentProvider,指定android.authorities=""(惟一標識)
  • update、insert、delete方法會引發數據源的改變,在這個時候就須要ContentResolver的notifyChange方法來通知外界的數據改變狀況,ContentResolver的registerContentObserver方法來註冊觀察者,經過unregisterContentObserver方法來接觸觀察者

6.使用Socket

        Socket是網絡通訊中的概念,他有TCP和UDP協議

        TCP是面向鏈接的協議,提供穩定的雙向通訊功能,他創建連接時須要進行三次握手 才能完成,爲了提供穩定的數據傳輸功能,其自己提供了超市重傳機制,所以具備很高的穩定性

        UDP是無鏈接的,提供不穩定的單向通訊功能,也能夠實現雙向通訊功能,缺點是不保證數據必定可以正確傳輸,尤爲是在網絡擁塞的狀況下

 

 

選擇適合的IPC:

相關文章
相關標籤/搜索