Android的IPC機制(四)—— Messenger的使用及原始碼分析
綜述
在前面幾篇中我們詳細的介紹了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只是在客戶端與服務端跨程式的傳遞資料,而不能夠去訪問服務端的方法。
原始碼下載
相關文章
- Android IPC 機制分析Android
- Android IPC機制(三):淺談Binder的使用Android
- android IPC 通訊(上)-sharedUserId&&MessengerAndroidMessenger
- Android 原始碼分析(二)handler 機制Android原始碼
- Android--Handler機制及原始碼詳解Android原始碼
- 原始碼分析:Android訊息處理機制原始碼Android
- 詳解 Android 中的 IPC 機制:基礎篇Android
- Android多程式之Messenger的使用AndroidMessenger
- Android 多程式之Messenger的使用AndroidMessenger
- 基於原始碼分析 Android View 繪製機制原始碼AndroidView
- ThreadPoolExecutor的使用及原始碼分析thread原始碼
- 02.Android之IPC機制問題Android
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- 「Android」分析EventBus原始碼擴充套件Weex事件機制Android原始碼套件事件
- Android AccessibilityService機制原始碼解析Android原始碼
- 談談JUC----------CAS機制及AtomicInteger原始碼分析原始碼
- debug:am trace-ipc原始碼分析原始碼
- Dubbo 原始碼分析 - SPI 機制原始碼
- React原始碼分析 – 事件機制React原始碼事件
- Android 8.0 原始碼分析 (四) Activity 啟動Android原始碼
- Android原始碼分析:手把手帶你深入瞭解Glide的快取機制Android原始碼IDE快取
- android的TouchEvent派發機制的分析Android
- 全面剖析Android訊息機制原始碼Android原始碼
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- HashMap擴容機制原始碼分析HashMap原始碼
- 從原始碼分析Hystrix工作機制原始碼
- DRF檢視的使用及原始碼流程分析原始碼
- Android事件分發:從原始碼角度分析View事件分發機制Android事件原始碼View
- Dubbo原始碼分析(六)Dubbo通訊的編碼解碼機制原始碼
- Android Messenger原理AndroidMessenger
- Android Handler訊息機制原始碼解讀Android原始碼
- Android 9.0 原始碼_機制篇 -- 全面解析 HandlerAndroid原始碼
- Buffer的建立及使用原始碼分析——ByteBuffer為例原始碼
- OkHttp 原始碼分析(二)—— 快取機制HTTP原始碼快取
- JVMTI Attach機制與核心原始碼分析JVM原始碼
- RecyclerView 原始碼分析(二) —— 快取機制View原始碼快取
- Dubbo原始碼分析(一)Dubbo的擴充套件點機制原始碼套件
- SPI機制剖析——基於DriverManager+ServiceLoader的原始碼分析原始碼
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼