Android 進程間通訊——Service、Messenger

概述

介紹綁定服務端的三種方式:同一進程綁定服務、跨進程綁定服務(Messenger)、跨進程綁定服務(aidl)。 重點說一下經過Messenger、Service實現的進程間通訊。

詳細

1、準備工做

 

開發環境:html

jdk1.8java

Eclipse Luna Service Release 1 (4.4.1)android

運行環境:編程

華爲榮耀6(Android4.4)、華爲p9(Android7.0)安全

實現功能:服務器

Messenger、Service實現的進程間通訊多線程

2、基礎知識

bound服務是客戶端 - 服務器結構中的服務器。 bound服務容許組件(如Activity)綁定到服務,發送請求,接收響應,甚至執行進程間通訊(IPC)。 綁定的服務一般僅服務於另外一個應用程序組件,而且不會無限期地在後臺運行。dom

bound服務是Service類的一個實現,它容許其餘應用程序綁定到它並與之交互。 要提供綁定服務,您必須實現onBind()回調方法。 此方法返回一個IBinder對象,該對象定義了客戶端與服務進行交互的編程接口。異步

客戶端能夠經過調用bindService()綁定到該服務。當它這樣作時,它必須實現ServiceConnection,監視與服務的鏈接。 bindService()方法當即返回,沒有返回值,可是當Android系統建立客戶端和服務之間的鏈接時,它會回調ServiceConnection上的onServiceConnected()來傳遞給客戶端IBinder對象,客戶端可經過IBinder對象與服務通訊。ide

多個客戶端能夠所有鏈接到該服務。可是,系統只會在第一個客戶端綁定時調用您的服務的onBind()方法來檢索IBinder。系統而後將相同的IBinder傳遞給綁定的任何其餘客戶端,而無需再次調用onBind()。

當最後一個客戶端解除綁定服務時,系統會銷燬該服務(除非該服務也由startService()啓動)。

實現綁定服務時,最重要的部分是定義onBind()回調方法返回的接口。您能夠經過幾種不一樣的方法來定義服務的IBinder接口,如下部分將討論每種技術。

3、擴展Binder類

在建立提供綁定的Service時,必須提供一個IBinder (客戶端能夠用來與服務進行交互的編程接口)。有三種方法能夠定義接口:

一、擴展Binder類

若是您的Service對您本身的應用程序是私有的,而且與客戶端在相同的進程中運行(這是常見的),則應該經過擴展Binder類並建立其實例,onBind()返回該實例。 客戶端收到Binder,可使用它直接訪問Binder實現或甚至Service中可用的公共方法。

當您的服務只是您本身的應用程序的後臺工做者時,這是首選技術。您不會以這種方式建立界面的惟一緣由是由於您的服務被其餘應用程序或單獨的進程使用。

二、使用Messenger

若是您須要Service 和客戶端位於不一樣的進程,則可使用Messenger爲服務建立一個interface。 以這種方式,服務定義響應不一樣類型的Message對象的Handler。 該Handler是Messenger的基礎,能夠與客戶端共享IBinder,容許客戶端使用Message對象向服務發送命令。 此外,客戶端能夠定義本身的Messenger,所以服務能夠發回消息。

這是執行進程間通訊(IPC)的最簡單的方法,由於Messenger將全部請求排隊到單個線程中,以便您沒必要將服務設計爲線程安全。

三、使用AIDL

AIDL(Android Interface Definition Language)執行全部的工做,將對象分解爲基元,操做系統能夠在進程之間瞭解和編組它們以執行IPC。 以前使用的Messenger技術其實是基於AIDL做爲其底層結構。 如上所述,Messenger在單個線程中建立全部客戶端請求的隊列,所以服務一次接收一個請求。 可是,若是您但願您的服務同時處理多個請求,則能夠直接使用AIDL。 在這種狀況下,您的服務必須可以進行多線程並創建線程安全。

要直接使用AIDL,您必須建立一個定義編程接口的.aidl文件。 Android SDK工具使用此文件生成一個實現接口並處理IPC的抽象類,而後您能夠在服務中擴展它。

注意:大多數應用程序不該該使用AIDL建立綁定的服務,由於它可能須要多線程功能,並可能致使更復雜的實現。所以,AIDL不適用於大多數應用程序。

4、擴展Binder類

若是您的服務僅由本地應用程序使用,而且不須要跨進程工做,那麼您能夠實現本身的Binder類,爲客戶端直接訪問服務中的公共方法。

注意:只有當客戶端和服務處於相同的應用程序和進程中時,這是最多見的。 例如,將Activity綁定到其本身的Service對於須要在後臺播放音樂的音樂應用程序將是有效的。

具體使用方法:

一、在您的服務中,建立一個或多個實例Binder: 
包含客戶端能夠調用的公共方法 
返回當前Service實例,該實例具備客戶端能夠調用的公共方法 
或者,使用客戶端能夠調用的公共方法返回由服務託管的另外一個類的實例

二、Binder從onBind()回調方法返回此實例。

三、在客戶端中,Binder從onServiceConnected()回調方法接收並使用提供的方法調用綁定的服務。

如下是一種經過Binder實現爲客戶端訪問服務中的方法的服務:

public class LocalService extends Service {
    // 建立IBinder對象
    private final IBinder mBinder = new LocalBinder();    // Random number generator
    private final Random mGenerator = new Random();    /**
     * 用於客戶端綁定的類。 由於咱們知道這個服務老是和客戶端同樣運行在統一進程,
     * 因此咱們不須要處理IPC
     */
    public class LocalBinder extends Binder {
        LocalService getService() {            //返回此LocalService實例,以便客戶端能夠調用公共方法
            return LocalService.this;
        }
    }    @Override
    public IBinder onBind(Intent intent) {        //返回LocalBinder實例
        return mBinder;
    }    /** method for clients */
    public int getRandomNumber() {      return mGenerator.nextInt(100);
    }
}

LocalBinder類繼承了Binder,並提供了getService()方法,該方法返回LocalService實例。 
LocalServcie的onBind方法返回LocalBinder實例。 
客戶端獲取該LocalBinder實例,而後經過調用getService()方法獲取LocalServcie。這容許客戶端調用該Service中的公共方法。例如,客戶端能夠從服務調用getRandomNumber()。 
如下是Activity的代碼,

public class BindingActivity extends Activity {
    LocalService mService;    boolean mBound = false;    @Override
    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }    @Override
    protected void onStart() {        super.onStart();        // 綁定LocalService
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }    @Override
    protected void onStop() {        super.onStop();        // Unbind from the service
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }
    }    //單擊按鈕時調用。
    public void onButtonClick(View v) {        if (mBound) {            // 調用LocalService的一個方法。
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }    /** Defines callbacks for service binding, passed to bindService() */
    private ServiceConnection mConnection = new ServiceConnection() {        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {            // 綁定到LocalService,轉換IBinder,獲取LocalService實例。
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }        @Override
        public void onServiceDisconnected(ComponentName com) {
            mBound = false;
        }
    };
}

5、進程間通訊(使用Messenger)

若是您須要服務與遠程進程通訊,那麼可使用Messenger爲服務提供接口。這種技術容許執行進程間通訊(IPC),而無需使用AIDL。

如下是Messenger使用總結:

A、Service實現一個Handler,接收客戶端發送的消息。

B、Handler用於建立一個Messenger對象(這是對Handler的引用)。

C、Messenger建立一個IBinder,Service將IBinder 從onBind()返回給客戶端。

D、客戶端使用IBinder來實例化Messenger (引用服務的Handler),客戶端利用Messenger將Message對象發送到服務。

E、Service接收 Message在其Handler的handleMessage()方法中進行處理。

以這種方式,客戶端沒有「方法」能夠調用該服務。相反,客戶端提供Service在其Handler中接收的「消息」(Message對象)。

以下是一個使用Messenger實現進程間通訊的實例。由兩個應用程序,一個是服務端工程——RemoteService,另外一個是客戶端工程——LocalClient。 

image.png

image.png

 

服務端

服務端沒有Activity(沒有界面),主要是一個Service——RemoteService。

服務端沒有Activity(沒有界面),主要是一個Service——RemoteService。 
1 service建立Handler對象。

/**
 * 處理來自客戶端的消息
 */
//建立Handler對象
private Handler mHandler = new Handler(){
    @Override
    public void handleMessage(Message msg) {
        Log.i(TAG, "msg.what="+msg.what);
        Log.i(TAG, "mHandler Thread ="+Thread.currentThread());
        switch (msg.what) {
            case MSG_SAY_HELLO:
                Toast.makeText(getApplicationContext(), "hello,remote service", Toast.LENGTH_SHORT).show();
                //經過message對象獲取客戶端傳遞過來的Messenger對象。
                Messenger messenger = msg.replyTo;
                if(messenger != null){
              Message messg = Message.obtain(null, MSG_SAY_HELLO);
              try {
                //向客戶端發送消息
            messenger.send(messg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
                }
                break;
            case MSG_MUSIC_PLAY:
                //播放音樂
                startPlay();
                break;
            case MSG_MUSIC_STOP:
                //中止播放
                stopPlay();
                break;
            default:
                break;
        }
    }
};

2 建立Messenger對象

//建立Mesenger 對象
Messenger mMessenger = new Messenger(mHandler);

3 onBind()返回IBinder對象

@Overridepublic IBinder onBind(Intent intent) {
    Log.d(TAG, "onBind");    //經過Mesenger對象獲取IBinder實例,並返回
    return mMessenger.getBinder();
}

4 服務端接收客戶端發送的消息,並在Handler對象的hanleMessage方法中進行處理。

4 服務端接收客戶端發送的消息,並在Handler對象的hanleMessage方法中進行處理。 
Handler接收客戶端發來的Message消息,並進行處理。能夠經過msg.replyTo獲取客戶端傳遞的Messenger對象(前提是客戶端設置了該對象),經過該Messenger對象能夠實現服務端向客戶端發送消息。 
播放音樂和中止播放的代碼就不貼了。

清單文件AndroidManifest.xml

<service
    android:name="com.zpengyong.service.RemoteService"
    android:enabled="true"
    android:exported="true">
    <intent-filter>
        <action android:name="com.zpengyong.messanger"></action>
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</service>

exported要設置爲true,不然別的進程不能使用該Service。

客戶端

應用程序組件(客戶端)能夠經過調用綁定到服務 bindService()。而後,

應用程序組件(客戶端)能夠經過調用綁定到服務 bindService()。而後,android系統調用該Service的onBind()方法,該方法返回一個IBinder用於與服務進行交互的方法。

綁定是異步的。bindService()當即返回,並不會返回IBinder給客戶端。要接收IBinder,客戶端必須建立一個實例ServiceConnection並將其傳遞給bindService()。在ServiceConnection包括系統調用提供的回調方法IBinder。

注意:只有活動,服務和內容提供商能夠綁定到服務 - 您沒法從廣播接收方綁定到服務。

1 綁定Service

其中intent的action和Service註冊的action保持一致。

private void bind() {
    mBindState = STATE_CONNECTING;
    Intent intent = new Intent();    // 設置action 與service在清單文件中的action保持一致
    intent.setAction("com.zpengyong.messanger");    // 綁定服務
    bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    mStateText.setText("connecting");
}

2 鏈接成功回調

實現ServiceConnection,必須覆蓋兩個回調方法:

一、onServiceConnected()

系統調用此方法來傳遞IBinder由服務onBind()方法返回的結果。

二、onServiceDisconnected()

當與服務的鏈接意外丟失時,例如服務已崩潰或已被殺死時,Android系統會調用此操做。當客戶端解除綁定(unbindService)時,這不被調用。

客戶端與遠端Servcie鏈接成功,系統會回調onServiceConnected()。經過IBinder參數獲取Messenger,該Messenger用來向Service發送消息。

private ServiceConnection mConnection = new ServiceConnection() {    // 當與服務端鏈接成功時,回調該方法。
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        Log.i(TAG, "onServiceConnected");        //經過IBinder參數獲取Messenger
        mRemoteMessenger = new Messenger(service);
        mStateText.setText("connected");
        mBindState = STATE_CONNECTED;
        mBtnBind.setText("解綁");
    }    // 當與服務端鏈接異常斷開時,回調該方法。
    @Override
    public void onServiceDisconnected(ComponentName name) {
        Log.i(TAG, "onServiceDisconnected");
        mRemoteMessenger = null;
        mStateText.setText("disconnected");
        mBindState = STATE_DISCONNECTED;
        mBtnBind.setText("綁定");
    }
};

3 向服務端發送消息 
Message屬性replyTo不是必須的,若是但願Service向客戶端發送消息則須要。

private void sendMsg(int what){    if (mRemoteMessenger == null)        return;    //建立消息,設置what屬性
    Message msg = Message.obtain(null, what);    //replyTo不是必須的,若是但願Service向客戶端發送消息則須要設置。
    msg.replyTo = mMessenger;    try {        //發送消息
        mRemoteMessenger.send(msg);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

sendMsg(MSG_SAY_HELLO); 向服務端打招呼。 
sendMsg(MSG_MUSIC_PLAY); 向服務端發送消息 播放音樂 
sendMsg(MSG_MUSIC_STOP); 向服務端發送消息 中止播放

4 接收Service的回覆

Handler中接收到Service發送到消息,進行處理。

private Handler mHandler = new Handler() {
    public void handleMessage(android.os.Message msg) {
        switch (msg.what) {
        case MSG_SAY_HELLO:
            Log.i(TAG,"MSG_SAY_HELLO");
            break;
        default:
            break;
        }
    };
};
//向Service發送消息所用
private Messenger mRemoteMessenger = null;
//接收Service的回覆所用
private Messenger mMessenger = new Messenger(mHandler);

5 解除綁定

須要斷開與服務的鏈接時,請調用unbindService()。

private void unbind() {
    mBindState = STATE_DISCONNECTING;    //解除與Service的鏈接
    unbindService(mConnection);
    mStateText.setText("disconnecting");
    mBindState = STATE_DISCONNECTED;
    mStateText.setText("disconnected");
    mBtnBind.setText("綁定");
}

unbindService與Service斷開鏈接以後,因爲我RemoteService是經過client的綁定開啓的,因此解除綁定後,RemoteService 會走onUnbind --》onDestroy。可是還有遠端Messenger對象,依然能夠向向Service發送消息(控制播放音樂、中止播放)。

6、管理綁定服務的生命週期

當一個Service從全部客戶端解除綁定時,Android系統會銷燬它(除非它是經過startServiceon建立的)。所以,您無需管理服務的生命週期,若是它徹底是一個有限的服務,Android系統將根據是否綁定到任何客戶端來管理您的服務。

另外,若是您的Service啓動並接受綁定,那麼當系統調用您的onUnbind()方法時,若是但願在下次客戶端綁定到服務時接收調用onRebind()(而不是調用onBind()),則能夠選擇返回 true。但客戶端仍然在onServiceConnected()回調中收到IBinder。下圖說明了這種生命週期的邏輯。 

image.png

7、程序代碼截圖

一、客戶端

image.png

二、服務端

image.png

8、運行效果

運行,右鍵項目:Run as -》Android Application

image.png

image.png

 

注:本文著做權歸做者,由demo大師發表,拒絕轉載,轉載須要做者受權

相關文章
相關標籤/搜索