Android 基於Message的進程間通訊 Messenger徹底解析

1、概述

說到Android進程間通訊,你們確定能想到的是編寫aidl文件,而後經過aapt生成的類方便的完成服務端,以及客戶端代碼的編寫。若是你對這個過程不熟悉,能夠查看Android aidl Binder框架淺析html

固然今天要說的通訊方式確定不是經過編寫aidl文件的方式,那麼有請今天的主角:Messenger。ok,這是什麼樣的一個類呢?咱們看下注釋java

This allows for the implementation of message-based communication across processesandroid

容許實現基於消息的進程間通訊的方式。服務器

那麼,什麼叫基於消息的進程間通訊方式呢?看個圖理解下:app

能夠看到,咱們能夠在客戶端發送一個Message給服務端,在服務端的handler中會接收到客戶端的消息,而後進行對應的處理,處理完成後,再將結果等數據封裝成Message,發送給客戶端,客戶端的handler中會接收處處理的結果。框架

這樣的進程間通訊是否是很爽呢?dom

  • 基於Message,相信你們都很熟悉
  • 支持回調的方式,也就是服務端處理完成長任務能夠和客戶端交互
  • 不須要編寫aidl文件

此外,還支持,記錄客戶端對象的Messenger,而後能夠實現一對多的通訊;甚至做爲一個轉接處,任意兩個進程都能經過服務端進行通訊,這個後面再說。ide

看到這,有沒有一些的小激動,咱們能夠不寫aidl文件,方便的實現進程間通訊了,是否是又能夠裝一下了。哈,下面看個簡單的例子。源碼分析


2、通訊實例

這個例子,經過兩個apk演示,一個apk是Server端,一個是Client端;佈局

(1) Server端

代碼

package com.imooc.messenger_server; import android.app.Service; import android.content.Intent; 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 static final int MSG_SUM = 0x110; //最好換成HandlerThread的形式 private Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msgfromClient) { Message msgToClient = Message.obtain(msgfromClient);//返回給客戶端的消息 switch (msgfromClient.what) { //msg 客戶端傳來的消息 case MSG_SUM: msgToClient.what = MSG_SUM; try { //模擬耗時 Thread.sleep(2000); msgToClient.arg2 = msgfromClient.arg1 + msgfromClient.arg2; msgfromClient.replyTo.send(msgToClient); } catch (InterruptedException e) { e.printStackTrace(); } catch (RemoteException e) { e.printStackTrace(); } break; } super.handleMessage(msgfromClient); } }); @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
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

服務端就一個Service,能夠看到代碼至關的簡單,只須要去聲明一個Messenger對象,而後onBind方法返回mMessenger.getBinder();

而後坐等客戶端將消息發送到handleMessage想法,根據message.what去判斷進行什麼操做,而後作對應的操做,最終將結果經過 msgfromClient.replyTo.send(msgToClient);返回。

能夠看到咱們這裏主要是取出客戶端傳來的兩個數字,而後求和返回,這裏我有意添加了sleep(2000)模擬耗時,注意在實際使用過程當中,能夠換成在獨立開闢的線程中完成耗時操做,好比和HandlerThread結合使用。

註冊文件

<service android:name=".MessengerService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.zhy.aidl.calc"></action> <category android:name="android.intent.category.DEFAULT" /> </intent-filter> </service>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

別忘了註冊service,寫完之後直接安裝。

(二)客戶端

Activity

package com.imooc.messenger_client; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; 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.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.LinearLayout; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private static final int MSG_SUM = 0x110; private Button mBtnAdd; private LinearLayout mLyContainer; //顯示鏈接狀態 private TextView mTvState; private Messenger mService; private boolean isConn; private Messenger mMessenger = new Messenger(new Handler() { @Override public void handleMessage(Message msgFromServer) { switch (msgFromServer.what) { case MSG_SUM: TextView tv = (TextView) mLyContainer.findViewById(msgFromServer.arg1); tv.setText(tv.getText() + "=>" + msgFromServer.arg2); break; } super.handleMessage(msgFromServer); } }); private ServiceConnection mConn = new ServiceConnection() { @Override public void onServiceConnected(ComponentName name, IBinder service) { mService = new Messenger(service); isConn = true; mTvState.setText("connected!"); } @Override public void onServiceDisconnected(ComponentName name) { mService = null; isConn = false; mTvState.setText("disconnected!"); } }; private int mA; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //開始綁定服務 bindServiceInvoked(); mTvState = (TextView) findViewById(R.id.id_tv_callback); mBtnAdd = (Button) findViewById(R.id.id_btn_add); mLyContainer = (LinearLayout) findViewById(R.id.id_ll_container); mBtnAdd.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { int a = mA++; int b = (int) (Math.random() * 100); //建立一個tv,添加到LinearLayout中 TextView tv = new TextView(MainActivity.this); tv.setText(a + " + " + b + " = caculating ..."); tv.setId(a); mLyContainer.addView(tv); Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); msgFromClient.replyTo = mMessenger; if (isConn) { //往服務端發送消息 mService.send(msgFromClient); } } catch (RemoteException e) { e.printStackTrace(); } } }); } private void bindServiceInvoked() { Intent intent = new Intent(); intent.setAction("com.zhy.aidl.calc"); bindService(intent, mConn, Context.BIND_AUTO_CREATE); Log.e(TAG, "bindService invoked !"); } @Override protected void onDestroy() { super.onDestroy(); unbindService(mConn); } } 
  • 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
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134

代碼也不復雜,首先bindService,而後在onServiceConnected中拿到回調的service(IBinder)對象,經過service對象去構造一個mService =new Messenger(service);而後就可使用mService.send(msg)給服務端了。

咱們消息的發送在Btn.onclick裏面:

Message msgFromClient = Message.obtain(null, MSG_SUM, a, b); msgFromClient.replyTo = mMessenger; if (isConn) { //往服務端發送消息 mService.send(msgFromClient); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

那麼服務端會收到消息,處理完成會將結果返回,傳到Client端的mMessenger中的Handler的handleMessage方法中。

佈局文件

<LinearLayout android:id="@+id/id_ll_container" 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:orientation="vertical" 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=".MainActivity"> <TextView android:id="@+id/id_tv_callback" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Messenger Test!"/> <Button android:id="@+id/id_btn_add" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="add"/> </LinearLayout> 
  • 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

效果圖

能夠看到,咱們每點擊一次按鈕,就往服務器發送個消息,服務器拿到消息執行完成後,將結果返回。

整個通訊的代碼看起來仍是至關的清爽的,那麼你們有沒有對其內部的原理有一絲的好奇呢?下面咱們就來看下其內部是如何實現的。

對了,源碼分析前,這裏插一句,你們經過代碼能夠看到服務端往客戶端傳遞數據是經過msg.replyTo這個對象的。那麼服務端徹底能夠作到,使用一個List甚至Map去存儲全部綁定的客戶端的msg.replyTo對象,而後想給誰發消息均可以。甚至能夠把A進程發來的消息,經過B進程的msg.replyTo發到B進程那裏去。相關代碼呢,能夠參考官方的文檔:service,注意下拉找:Remote Messenger Service Sample。


3、源碼分析

其實Messenger的內部實現的,實際上也是依賴於aidl文件實現的。

(一)首先咱們看客戶端向服務端通訊

服務端

服務端的onBind是這麼寫的:

public IBinder onBind(Intent intent) { return mMessenger.getBinder(); }
  • 1
  • 2
  • 3
  • 4

那麼點進去:

public IBinder getBinder() { return mTarget.asBinder(); }
  • 1
  • 2
  • 3

能夠看到返回的是mTarget.asBinder();

mTarget是哪來的呢?

別忘了咱們前面去構造mMessenger對象的代碼:new Messenger(new Handler())

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

原來是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

mTarget是一個MessengerImpl對象,那麼asBinder其實是返回this,也就是MessengerImpl對象;

這是個內部類,能夠看到繼承自IMessenger.Stub,而後實現了一個send方法,該方法就是將接收到的消息經過 Handler.this.sendMessage(msg);發送到handleMessage方法。

看到這,你們有沒有想到什麼,難道不以爲extends IMessenger.Stub這種寫法異常的熟悉麼?

咱們傳統寫aidl文件,aapt給咱們生成什麼,生成IXXX.Stub類,而後咱們服務端繼承IXXX.Stub實現接口中的方法。

沒錯,其實這裏內部其實也是依賴一個aidl生成的類,這個aidl位於:frameworks/base/core/java/android/os/IMessenger.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

看到這,你應該明白了,Messenger並無什麼神奇之處,實際上,就是依賴該aidl文件生成的類,繼承了IMessenger.Stub類,實現了send方法,send方法中參數會經過客戶端傳遞過來,最終發送給handler進行處理。這裏不理解,請詳細看下Android aidl Binder框架淺析

客戶端

客戶端首先經過onServiceConnected拿到sevice(Ibinder)對象,這裏沒什麼特殊的,咱們平時的寫法也是這樣的,只不過咱們平時會這麼寫:

IMessenger.Stub.asInterface(service)拿到接口對象進行調用;

而,咱們的代碼中是

mService = new Messenger(service);

跟進去,你會發現:

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

soga,和咱們平時的寫法如出一轍!

到這裏就能夠明白,客戶端與服務端通訊,實際上和咱們平時的寫法沒有任何區別,經過編寫aidl文件,服務端onBind利用Stub編寫接口實現返回;客戶端利用回調獲得的IBinder對象,使用IMessenger.Stub.asInterface(target)拿到接口實例進行調用(內部實現,參考Android aidl Binder框架淺析)。

(2)服務端與客戶端通訊

那麼,客戶端與服務端通訊的確沒什麼特殊的地方,咱們徹底也能夠編寫個相似的aidl文件實現;那麼服務端是如何與客戶端通訊的呢?

還記得,客戶端send方法發送的是一個Message,這個Message.replyTo指向的是一個mMessenger,咱們在Activity中初始化的。

那麼將消息發送到服務端,確定是經過序列化與反序列化拿到Message對象,咱們看下Message的反序列化的代碼:

# Message

private void readFromParcel(Parcel source) { what = source.readInt(); arg1 = source.readInt(); arg2 = source.readInt(); if (source.readInt() != 0) { obj = source.readParcelable(getClass().getClassLoader()); } when = source.readLong(); data = source.readBundle(); replyTo = Messenger.readMessengerOrNullFromParcel(source); sendingUid = source.readInt(); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

主要看replyTo,調用的是Messenger.readMessengerOrNullFromParcel

public static Messenger readMessengerOrNullFromParcel(Parcel in) { IBinder b = in.readStrongBinder(); return b != null ? new Messenger(b) : null; } public static void writeMessengerOrNullToParcel(Messenger messenger, Parcel out) { out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder() : null); }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

經過上面的writeMessengerOrNullToParcel能夠看到,它將客戶端的messenger.mTarget.asBinder()對象進行了恢復,客戶端的message.mTarget.asBinder()是什麼?

客戶端也是經過Handler建立的Messenger,因而asBinder返回的是:

public Messenger(Handler target) { mTarget = target.getIMessenger(); } 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); } } public IBinder getBinder() { return mTarget.asBinder(); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

那麼asBinder,實際上就是MessengerImpl extends IMessenger.Stub中的asBinder了。

#IMessenger.Stub

@Override public android.os.IBinder asBinder() { return this; }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

那麼其實返回的就是MessengerImpl對象本身。到這裏能夠看到message.mTarget.asBinder()其實返回的是客戶端的MessengerImpl對象。

最終,發送給客戶端的代碼是這麼寫的:

msgfromClient.replyTo.send(msgToClient);

public void send(Message message) throws RemoteException { mTarget.send(message); } 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

這個mTarget實際上就是對客戶端的MessengerImpl對象的封裝,那麼send(message)(屏蔽了transact/onTransact的細節),這個message最終確定傳到客戶端的handler的handleMessage方法中。

好了,到此咱們的源碼分析就結束了~~

總結下:

  • 客戶端與服務端通訊,利用的aidl文件,沒什麼特殊的
  • 服務端與客戶端通訊,主要是在傳輸的消息上作了處理,讓Messager.replyTo指向的客戶端的Messenger,而Messenger又持有客戶端的一個Binder對象(MessengerImpl)。服務端正是利用這個Binder對象作的與客戶端的通訊。

能夠考慮本身編寫aidl文件,實現下服務端對客戶端的回調。

相關文章
相關標籤/搜索