Android的IPC機制(四)—— Messenger的使用及原始碼分析

yangxi_001發表於2017-12-19

綜述

  在前面幾篇中我們詳細的介紹了AIDL的使用及原理。在這裡我們感覺到AIDL的在使用過程中還是比較複雜的,那麼有沒有一種簡單的方法來實現程式間的通訊呢?當然是有的,那就是利用Messenger。Messenger翻譯為信使,從他的名字就可以看出這個Messenger就是作為傳遞訊息用的。那麼我們就來看一下這個Messenger到底是如何使用的,以及在它內部是如何實現的。

Messenger的使用

使用步驟

  其實對於Messenger用起來是非常簡單的,那麼我們首先來看一下這個Messenger的使用步驟: 
  1. 在服務端我們實現一個 Handler,接收來自客戶端的每個呼叫的回撥 
  2. 這個Handler 用於建立 Messenger 物件(也就是對 Handler 的引用) 
  3. 用Messenger 建立一個 IBinder,服務端通過 onBind() 使其返回客戶端 
  4. 客戶端使用 IBinder 將 Messenger(引用服務的 Handler)例項化,然後使用後者將 Message 物件傳送給服務端 
  5. 服務端在其 Handler 中(具體地講,是在 handleMessage() 方法中)接收每個 Message 
  在這裡我們寫一個例子來更詳細的說明這個Messenger的使用。  

演示

  在這裡我們寫一個demo,我們通過客戶端隨機產生一個由小寫字母組成的10位字串傳送給服務端,由服務端轉換為大寫後再傳送給客戶端。 
這裡寫圖片描述

原始碼

  下面是服務端程式碼。

package com.ljd.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;

public class MessengerService extends Service {

    private final Messenger mMessenger = new Messenger(new ServiceHandler());
    private class ServiceHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 0:
                    Messenger clientMessenger = msg.replyTo;
                    Message replyMessage = Message.obtain();
                    replyMessage.what = 1;
                    Bundle bundle = new Bundle();
                    //將接收到的字串轉換為大寫後傳送給客戶端
                    bundle.putString("service", msg.getData().getString("client").toUpperCase());
                    replyMessage.setData(bundle);
                    try {
                        clientMessenger.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

  下面我們使服務端執行在獨立的程式中。

        <service
            android:name=".MessengerService"
            android:process=":remote">
        </service>
  • 1
  • 2
  • 3
  • 4

  下面是客戶端程式碼。

package com.ljd.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.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.Random;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class MainActivity extends AppCompatActivity {

    @Bind(R.id.messenger_linear)
    LinearLayout mShowLinear;

    private Messenger mMessenger;
    private final String LETTER_CHAR = "abcdefghijkllmnopqrstuvwxyz";




    private class ClientHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 1:
                    TextView textView = new TextView(MainActivity.this);
                    textView.setText("convert ==>:"
                            + (msg.getData().containsKey("service")?msg.getData().getString("service"):""));
                    mShowLinear.addView(textView);
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mMessenger = new Messenger(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            bindService();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        bindService();
    }

    @OnClick({R.id.test_button,R.id.clear_button})
    public void onClickButton(View v){
        switch (v.getId()){
            case R.id.test_button:
                sendToService();
                break;
            case R.id.clear_button:
                mShowLinear.removeAllViews();
                break;
        }
    }
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mConnection);
        ButterKnife.unbind(this);
    }

    private void bindService(){
        Intent intent = new Intent(MainActivity.this,MessengerService.class);
        bindService(intent,mConnection, Context.BIND_AUTO_CREATE);
    }

    private void sendToService(){
        Message messageClient = Message.obtain(null,0);
        //用於傳遞給服務端回覆的Messenger
        Messenger replyMessenger = new Messenger(new ClientHandler());
        Bundle bundle = new Bundle();
        bundle.putString("client",generateMixString());
        messageClient.setData(bundle);
        //通過Message的replyTo屬性將Messenger物件傳遞到服務端
        messageClient.replyTo = replyMessenger;
        TextView textView = new TextView(MainActivity.this);
        textView.setText("send:" + (bundle.getString("client")));
        mShowLinear.addView(textView);
        try {
            mMessenger.send(messageClient);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }
    /**
     * 隨機生成10位小寫字母的字串
     * @return
     */
    public String generateMixString() {
        StringBuffer sb = new StringBuffer();
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            sb.append(LETTER_CHAR.charAt(random.nextInt(LETTER_CHAR.length())));
        }
        return sb.toString();
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125

  注意:我們可以看到在Messenger中通過Message進行傳遞資料的。在Message中的欄位obj是不能用於程式間通訊的。 
  

Messenger原理分析

  首先我們進入Messenger這個類裡面看一下這個Messenger到底是什麼東西。

package android.os;

public final class Messenger implements Parcelable {
    private final IMessenger mTarget;

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

    public IBinder getBinder() {
        return mTarget.asBinder();
    }

    public boolean equals(Object otherObj) {
        if (otherObj == null) {
            return false;
        }
        try {
            return mTarget.asBinder().equals(((Messenger)otherObj)
                    .mTarget.asBinder());
        } catch (ClassCastException e) {
        }
        return false;
    }

    public int hashCode() {
        return mTarget.asBinder().hashCode();
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeStrongBinder(mTarget.asBinder());
    }

    public static final Parcelable.Creator<Messenger> CREATOR
            = new Parcelable.Creator<Messenger>() {
        public Messenger createFromParcel(Parcel in) {
            IBinder target = in.readStrongBinder();
            return target != null ? new Messenger(target) : null;
        }

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

    public static void writeMessengerOrNullToParcel(Messenger messenger,
            Parcel out) {
        out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
                : null);
    }

    public static Messenger readMessengerOrNullFromParcel(Parcel in) {
        IBinder b = in.readStrongBinder();
        return b != null ? new Messenger(b) : null;
    }

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69

  我們可以看到這個Messenger實現Parcelable介面。然後宣告瞭一個IMessenger 物件mTarget。並且在Messenger中的兩個構造方法中都對這個mTarget進行了初始化。那麼這個IMessenger 是什麼東西呢?我們首先看一下Messenger中的第一個構造方法。

public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}
  • 1
  • 2
  • 3

  首先我們使用這個構造方法在服務端建立一個Messenger物件,在onBind中通過Messenger中的getBinder方法向客戶端返回一個Binder物件。而在這個構造方法裡面通過Handler中的getIMessenger方法初始化mTarget。那麼我們進入Handler中看一下這個getIMessenger方法。 

public class Handler {
    ...

    final IMessenger getIMessenger() {
        synchronized (mQueue) {
            if (mMessenger != null) {
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }

    private final class MessengerImpl extends IMessenger.Stub {
        public void send(Message msg) {
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }

    ...
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

  這回我們看到了這個getIMessenger方法中返回了一個MessengerImpl物件,而這個MessengerImpl中對send方法實現,也只不過是通過Handler傳送了一個message。並且在Handler中的handleMessage方法中進行處理。 
  而在我們的客戶端當中,我們通過客戶端的onServiceConnected中拿到服務端返回的Binder物件,並且在onServiceConnected方法中new Messenger(service)去獲取這個IMessenger物件,下面我們就看一下這個Messenger的第二個構造方法。 

public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}
  • 1
  • 2
  • 3

  這個構造方法一般用於客戶端中。並且到現在我們一路走下來似乎看到了許多熟悉的身影IMessenger.Stub,Stub.asInterface等等。這些不正是我們在AIDL中使用到的嗎?於是我們猜想這個IMessenger應該是一個AIDL介面。現在就去找找這個IMessenger。這個IMessenger位於frameworks的base中, 完整路徑為frameworks/base/core/java/android/os/。我們就看一下這個AIDL介面,

package android.os;

import android.os.Message;

/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

  在這個IMessenger介面中只有一個send方法。而在這裡關鍵字oneway表示當服務使用者請求相應功能的時後不需要等待應答就可以直接呼叫返回,這個關鍵字可以用於介面宣告或者方法宣告語句中,如果介面宣告語句中使用了oneway關鍵字,則這個介面中宣告的所有方法都採用了oneway方式。這回我們也就明白這個Messenger了,其實這個Messenger就是進一步對AIDL進行了一次封裝。 
  在這裡客戶端通過服務端返回的Binder建立了Messenger物件。然後我們只需要建立一個Message物件,在這個Message物件中攜帶我們所需要傳遞給服務端的資料,這時候就可以在服務端中的handleMessage進行處理。若是我們需要服務端給我們返回資料,只需要在客戶端建立一個Handler,並且使用這個Handler建立一個Messenger物件,然後將這個Messenger物件通過Message中的replyTo欄位傳遞到服務端,在服務端獲取到客戶端的Messenger物件後,便可以通過這個Messenger傳送給客戶端,然後在客戶端中的handlerMessage處理即可。

AIDL與Messenger的區別

  我們可以發現在IPC通訊中使用Messenger要比使用AIDL簡單很多。因為在Messenger中是通過Handler來對AIDL進行的封裝,也就是說Messenger是通過佇列來呼叫服務的,而單純的AIDL會同時像服務端發出請求,這時候我們就必須對多執行緒進行處理。 
  那麼對於一個應用來說。它的Service不需要執行多執行緒我們應該去使用這個Messenger,返過來若是我們Service處理是多執行緒的我們就應該使用AIDL去定義介面。

總結

  Messenger它是一種輕量級的IPC 方法,我們使用起來也是非常的簡單。他是使用Handler對AIDL進行了一次封裝,一次只能處理一個請求。並且Messenger在傳送Message的時候不能使用他的obj欄位,我們可以用bundle來代替。最後還有一點就是Messenger只是在客戶端與服務端跨程式的傳遞資料,而不能夠去訪問服務端的方法。

原始碼下載

相關文章