Android基于Message的進程間通信之Messenger使用及解析

1膘流、Messenger 簡介

說到Android進程間通信樱衷,大家肯定能想到的是編寫aidl文件膀曾,然后通過aapt生成的類方便的完成服務端,以及客戶端代碼的編寫墙基。如果你對這個過程不熟悉软族,可以查看Android aidl Binder框架淺析;

當然今天要說的通信方式肯定不是通過編寫aidl文件的方式残制,那么有請今天的主角:Messenger立砸。ok,這是什么樣的一個類呢初茶?我們看下注釋

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

允許實現基于消息的進程間通信方式颗祝。

平時一說進程間通訊,大家都會想到AIDL纺蛆,其實messenger和AIDL作用一樣吐葵,都可以進行進程間通訊。它是基于消息的進程間通信桥氏,就像子線程和UI線程發(fā)送消息那樣温峭,是不是很簡單,還不用去寫AIDL文件字支,是不是有點小爽凤藏。哈哈。

此外堕伪,還支持記錄客戶端對象的Messenger揖庄,然后可以實現一對多的通信;甚至作為一個轉接處欠雌,任意兩個進程都能通過服務端進行通信蹄梢。

與 AIDL 比較:

當您需要執(zhí)行 IPC 時,為您的接口使用 Messenger 要比使用 AIDL 實現更加簡單富俄,因為 Messenger 會將所有服務調用排入隊列禁炒,而純粹的 AIDL 接口會同時向服務發(fā)送多個請求,服務隨后必須應對多線程處理霍比。

對于大多數應用幕袱,服務不需要執(zhí)行多線程處理,因此使用 Messenger 可讓服務一次處理一個調用悠瞬。如果您的服務必須執(zhí)行多線程處理们豌,則應使用 AIDL 來定義接口涯捻。

2、通信實例

1)Service端

代碼

package com.ryg.chapter_2.messenger;

import android.app.Service;
import android.content.Intent;
import android.os.*;
import android.util.Log;
import com.ryg.chapter_2.utils.MyConstants;

/**
 * FileName:
 * Author: nanzong
 * Date: 2019-06-20 17:23
 * Description:
 * History:
 */
public class MessengerService extends Service {

    private static final String TAG = "MessengerActivity";

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MyConstants.MSG_FROM_CLIENT:
                    Log.d(TAG, "receive msg from Client:" + msg.getData().getString("mymsg"));
                    Messenger msgfromClient = msg.replyTo;
                    Message relpyMessage = Message.obtain(null, MyConstants.MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply", "  嗯 望迎, 你的消息我已經收到障癌,稍后會回復你!");
                    relpyMessage.setData(bundle);
                    try {
                        msgfromClient.send(relpyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

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

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

}

服務端就是一個Service,只需要去聲明一個Messernger (Handler handler)對象擂煞,然后onBind方法中返回一個mMessenger.getBinder();

然后等客戶端將消息發(fā)送到Handler中的handleMessage混弥,根據msg.what去判斷什么操作,最終將結果通過msgfromClient.replyTo.send(relpyMessage);返回

注冊文件

  <service
                android:name=".messenger.MessengerService"
                android:process=":remote" >
            <intent-filter>
                <action android:name="com.ryg.MessengerService.launch" />
            </intent-filter>
        </service>

別忘了注冊service

2)客戶端

Activity

package com.ryg.chapter_2.messenger;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.*;
import android.util.Log;
import com.ryg.chapter_2.R;
import com.ryg.chapter_2.utils.MyConstants;

public class MessengerActivity extends Activity {

    private static final String TAG = "MessengerActivity";

    private Messenger mServerMessenger;
    @SuppressLint("HandlerLeak")
    private Messenger mClientMessenger = new Messenger(new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MyConstants.MSG_FROM_SERVICE:
                    Log.d(TAG, "receive msg from Service:" + msg.getData().getString("reply"));
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    });


    private ServiceConnection mConnection = new ServiceConnection() {

        //Activity 與 Service連接成功使回調該方法
        @Override
        public void onServiceConnected(ComponentName className, IBinder service) {
            mServerMessenger = new Messenger(service);
            Log.d(TAG, "bind service");
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("mymsg", "hello, this is client.");
            msg.setData(data);
            msg.replyTo = mClientMessenger;  //指定回信人是客戶端定義的

            try {
                mServerMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        //Activity 與 Service 斷開連接  回調該方法
        @Override
        public void onServiceDisconnected(ComponentName name) {
            mServerMessenger = null;
        }
    };

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

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

首先bindService,然后再onServiceConnected中拿到回調的service( Binder)對象对省,通過 mServerMessenger = new Messenger(service);然后就可以使用mServerMessenger.send(msg);發(fā)送給服務端了

     mServerMessenger = new Messenger(service);
            Log.d(TAG, "bind service");
            Message msg = Message.obtain(null, MyConstants.MSG_FROM_CLIENT);
            Bundle data = new Bundle();
            data.putString("mymsg", "hello, this is client.");
            msg.setData(data);
            msg.replyTo = mClientMessenger;  //指定回信人是客戶端定義的

            try {
                往服務端發(fā)送消息
                mServerMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }

那么服務端會收到消息蝗拿,處理完成會將結果返回,傳到client的mMessenger中的Handler的handleMessage方法中給對應地方進行處理

3) 運行結果

2019-06-28 08:56:57.480 21990-21990/com.ryg.chapter_2 D/MessengerActivity: bind service
2019-06-28 08:56:59.482 22039-22039/com.ryg.chapter_2:remote D/MessengerActivity: receive msg from Client:hello, this is client.
2019-06-28 08:56:59.482 21990-21990/com.ryg.chapter_2 D/MessengerActivity: receive msg from Service:  嗯 蒿涎, 你的消息我已經收到哀托,稍后會回復你!

通過代碼可以看到服務端往客戶端傳遞數據是通過msg.replyTo這個對象的劳秋,那么服務端完全可以做到仓手,使用一個List甚至Map去存儲所有綁定客戶端的msg.replyTo對象,然后想給誰發(fā)消息都可以玻淑。

3嗽冒、源碼分析

Messenger 有兩個構造函數

  • 以Handler 為參數
  • 以IBinder 為參數
//服務端構建對象
public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}
//客戶端構建對象
public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);    //和前面的 AIDL 很相似吧
}

1)客戶端向服務端通信

服務端

服務端的onBind是這么寫的:

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

那么點進去:

    /**
     * Retrieve the IBinder that this Messenger is using to communicate with
     * its associated Handler.
     * 
     * @return Returns the IBinder backing this Messenger.
     */
    public IBinder getBinder() {
        return mTarget.asBinder();
    }

可以看到返回的是mTarget.asBinder();
我們前邊構建Messenger對象的代碼 new Messenger(new Handler());
mTarget就是這么來的

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

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);
        }
    }

mTarget是一個MessengerImpl對象补履,那么asBinder實際上是返回this添坊,也就是MessengerImpl對象;

這是一個內部類箫锤,繼承IMessenger.Stub贬蛙,然后實現了一個send方法,該方法就是將接收到的消息通過Handler.this.sendMessage(msg);發(fā)送到handleMessage方法谚攒。

看到這阳准,大家有沒有想到什么,難道不覺得extends IMessenger.Stub這種寫法異常的熟悉么馏臭?

我們傳統(tǒng)寫aidl文件野蝇,aapt給我們生成什么,生成IXXX.Stub類括儒,然后我們服務端繼承IXXX.Stub實現接口中的方法浪耘。

這里依賴了一個aidl生成的類
/Users/nanzong/Library/Android/sdk/sources/android-28/android/os/IMessenger.aidl

package android.os;  

import android.os.Message;  

/** @hide */  
oneway interface IMessenger {  
    void send(in Message msg);  
}  

Messenger就是依賴了該aidl文件生成的類,繼承了該類的IMessenger.Stub類塑崖,實現了send方法,send方法中參數會通過客戶端傳遞過來痛倚,最終發(fā)送給Handler進行處理规婆。

客戶端

客戶端首先通過onServiceConnected拿到sevice(Ibinder)對象,這里沒什么特殊的,我們平時的寫法也是這樣的抒蚜,只不過我們平時會這么寫:

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

我們代碼中是

  mService = new Messenger(service);

  public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }

和我們平時aidl寫法沒有任何區(qū)別,通過編寫aidl文件嗡髓,服務端onBind利用Stub編寫接口實現返回操漠;客戶端利用回調得到IBinder對象,使用IMessager.Stub.asInterface(target)拿到接口實例進行調用饿这;

2)服務端與客戶端通信

那么浊伙,客戶端與服務端通信的確沒什么特殊的地方,我們完全也可以編寫個類似的aidl文件實現长捧;那么服務端是如何與客戶端通信的呢嚣鄙?

還記得,客戶端send方法發(fā)送的是一個Message串结,這個Message.replyTo指向的是一個mMessenger哑子,我們在Activity中初始化的。

那么將消息發(fā)送到服務端肌割,肯定是通過序列化與反序列化拿到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();
    }

主要看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);
    }

通過上面的writeMessengerOrNullToParcel可以看到把敞,它將客戶端的messenger.mTarget.asBinder()對象進行了恢復弥奸,客戶端的message.mTarget.asBinder()是什么?

客戶端也是通過Handler創(chuàng)建的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();
    }

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

#IMessenger.Stub

@Override 
public android.os.IBinder asBinder(){
return this;
}

那么其實返回的就是MessengerImpl對象自己伸蚯。到這里可以看到message.mTarget.asBinder()其實返回的是客戶端的MessengerImpl對象摩渺。

msgfromClient.replyTo.send(msgToClient);

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

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

總結下:

客戶端與服務端通信,利用的aidl文件挥萌,沒什么特殊的
服務端與客戶端通信绰姻,主要是在傳輸的消息上做了處理,讓Messager.replyTo指向的客戶端的Messenger,而Messenger又持有客戶端的一個Binder對象(MessengerImpl)引瀑。服務端正是利用這個Binder對象做的與客戶端的通信狂芋。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市憨栽,隨后出現的幾起案子帜矾,更是在濱河造成了極大的恐慌翼虫,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,561評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件屡萤,死亡現場離奇詭異珍剑,居然都是意外死亡,警方通過查閱死者的電腦和手機死陆,發(fā)現死者居然都...
    沈念sama閱讀 90,218評論 3 385
  • 文/潘曉璐 我一進店門招拙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人措译,你說我怎么就攤上這事别凤。” “怎么了瞳遍?”我有些...
    開封第一講書人閱讀 157,162評論 0 348
  • 文/不壞的土叔 我叫張陵闻妓,是天一觀的道長。 經常有香客問我掠械,道長由缆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,470評論 1 283
  • 正文 為了忘掉前任猾蒂,我火速辦了婚禮均唉,結果婚禮上,老公的妹妹穿的比我還像新娘肚菠。我一直安慰自己舔箭,他們只是感情好,可當我...
    茶點故事閱讀 65,550評論 6 385
  • 文/花漫 我一把揭開白布蚊逢。 她就那樣靜靜地躺著层扶,像睡著了一般。 火紅的嫁衣襯著肌膚如雪烙荷。 梳的紋絲不亂的頭發(fā)上镜会,一...
    開封第一講書人閱讀 49,806評論 1 290
  • 那天,我揣著相機與錄音终抽,去河邊找鬼戳表。 笑死,一個胖子當著我的面吹牛昼伴,可吹牛的內容都是我干的匾旭。 我是一名探鬼主播,決...
    沈念sama閱讀 38,951評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼圃郊,長吁一口氣:“原來是場噩夢啊……” “哼价涝!你這毒婦竟也來了?” 一聲冷哼從身側響起持舆,我...
    開封第一講書人閱讀 37,712評論 0 266
  • 序言:老撾萬榮一對情侶失蹤飒泻,失蹤者是張志新(化名)和其女友劉穎鞭光,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體泞遗,經...
    沈念sama閱讀 44,166評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,510評論 2 327
  • 正文 我和宋清朗相戀三年席覆,在試婚紗的時候發(fā)現自己被綠了史辙。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,643評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡佩伤,死狀恐怖聊倔,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情生巡,我是刑警寧澤耙蔑,帶...
    沈念sama閱讀 34,306評論 4 330
  • 正文 年R本政府宣布,位于F島的核電站孤荣,受9級特大地震影響甸陌,放射性物質發(fā)生泄漏。R本人自食惡果不足惜盐股,卻給世界環(huán)境...
    茶點故事閱讀 39,930評論 3 313
  • 文/蒙蒙 一钱豁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧疯汁,春花似錦牲尺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,745評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至溢豆,卻和暖如春蜒简,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背沫换。 一陣腳步聲響...
    開封第一講書人閱讀 31,983評論 1 266
  • 我被黑心中介騙來泰國打工臭蚁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人讯赏。 一個月前我還...
    沈念sama閱讀 46,351評論 2 360
  • 正文 我出身青樓垮兑,卻偏偏與公主長得像,于是被迫代替她去往敵國和親漱挎。 傳聞我的和親對象是個殘疾皇子系枪,可洞房花燭夜當晚...
    茶點故事閱讀 43,509評論 2 348