Android進(jìn)程間通信 Messenger詳解

1. 概念

Messenger,即進(jìn)程間通信的信使.它是基于Message的進(jìn)程間通信,我們可以像在線程間利用Handler.send(Message)一樣.

Messenger是一種輕量級(jí)的IPC方案,它的底層實(shí)現(xiàn)其實(shí)就是AIDL.跨進(jìn)程通信使用Messenger時(shí),Messenger會(huì)將所有服務(wù)調(diào)用加入隊(duì)列,然后服務(wù)端那邊一次處理一個(gè)調(diào)用,不會(huì)存在同時(shí)調(diào)用的情況.而AIDL則可能是多個(gè)調(diào)用同時(shí)執(zhí)行,必須處理多線程問題.

對(duì)于大多數(shù)應(yīng)用,跨進(jìn)程通信無(wú)需一對(duì)多,也就是無(wú)需執(zhí)行多線程處理,此時(shí)使用Messenger更適合.

2. 使用

2.1 大致流程

  1. 服務(wù)端實(shí)現(xiàn)一個(gè)Handler,由其接收來(lái)自客戶端的每個(gè)調(diào)用的回調(diào)
  2. 服務(wù)端使用Handler來(lái)創(chuàng)建Messenger對(duì)象
  3. Messenger創(chuàng)建一個(gè)IBinder,服務(wù)端通過onBind()將其返回給客戶端
  4. 客戶端使用IBinder將Messenger實(shí)例化,然后再用起將Message對(duì)象發(fā)送給服務(wù)端
  5. 服務(wù)端在其Handler#handleMessage()中,接收每個(gè)Message

2.2 案例

2.2.1 服務(wù)端

首先需要在服務(wù)端創(chuàng)建一個(gè)Handler用于接收消息,然后將此Handler傳遞給Messenger,并在onBind中將該Messenger的底層binder返回回去.

//這里服務(wù)端Service是運(yùn)行在單獨(dú)的進(jìn)程中的 android:process=":other"
class MessengerService : Service() {

    private lateinit var mMessenger: Messenger

    override fun onBind(intent: Intent): IBinder {
        log(TAG, "onBind~")
        //傳入Handler實(shí)例化Messenger
        mMessenger = Messenger(IncomingHandler(this))
        //將Messenger中的binder返回給客戶端,讓它可以遠(yuǎn)程調(diào)用
        return mMessenger.binder
    }

    //處理客戶端傳遞過來(lái)的消息(Message)  并根據(jù)what決定下一步操作
    internal class IncomingHandler(
        context: Context,
        private val applicationContext: Context = context.applicationContext
    ) : Handler(
        Looper.getMainLooper()
    ) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SAY_HELLO -> {
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
                    log(TAG, "hello!")
                }
                else -> super.handleMessage(msg)
            }
        }
    }
}

2.2.2 客戶端

客戶端進(jìn)程中,首先是需要綁定遠(yuǎn)程Service.綁定完成之后,在onServiceConnected()中拿到遠(yuǎn)程Service返回的IBinder對(duì)象,用此IBinder對(duì)象實(shí)例化客戶端這邊的Messenger.有了這個(gè)Messenger,就可以通過這個(gè)Messenger往服務(wù)端發(fā)送消息了.示例代碼如下:

class MessengerActivity : TitleBarActivity() {

    /** 與服務(wù)端進(jìn)行溝通的Messenger */
    private var mService: Messenger? = null

    /** 是否已bindService */
    private var bound: Boolean = false

    private val mServiceConnection = object : ServiceConnection {

        override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
            mService = Messenger(service)
            bound = true
        }

        override fun onServiceDisconnected(name: ComponentName?) {
            mService = null
            bound = false
        }
    }

    override fun getThisTitle(): CharSequence {
        return "Messenger"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_messenger)

        btnConnect.setOnClickListener {
            connectService()
        }
        btnSayHello.setOnClickListener {
            sayHello()
        }
    }

    private fun sayHello() {
        if (!bound) {
            return
        }
        //創(chuàng)建,并且發(fā)送一個(gè)message給服務(wù)端   Message中what指定為MSG_SAY_HELLO
        val message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
        try {
            mService?.send(message)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }
    }

    private fun connectService() {
        Intent().apply {
            action = "com.xfhy.messenger.Server.Action"
            setPackage("com.xfhy.allinone")
        }.also { intent ->
            bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        if (bound) {
            unbindService(mServiceConnection)
            bound = false
        }
    }

}

通過示例代碼我們知道客戶端通過Messenger與服務(wù)端進(jìn)行通信時(shí),必須將數(shù)據(jù)放入Message中,Messenger和Message都實(shí)現(xiàn)了Parcelable接口,因此是可以跨進(jìn)程傳輸?shù)?Message只能通過what、arg1、arg2、Bundle以及replyTo來(lái)承載需要傳遞的數(shù)據(jù),如果需要傳遞Serializable或者Parcelable的對(duì)象則可以放進(jìn)Bundle里面進(jìn)行傳遞,Bundle還支持其他大量的數(shù)據(jù)類型.

2.2.3 服務(wù)端向客戶端發(fā)送消息

有時(shí)候我們需要客戶端能響應(yīng)服務(wù)端發(fā)送的消息,此時(shí)我們只需要在上面的示例的基礎(chǔ)上簡(jiǎn)單修改即可.

服務(wù)端這邊每次收到消息,都回復(fù)一條消息給客戶端,方便測(cè)試

internal class IncomingHandler : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SAY_HELLO -> {
                    log(TAG, "hello!")
                    //客戶端的Messenger就是放在Message的replyTo中的
                    replyToClient(msg, "I have received your message and will reply to you later")
                }
                MSG_TRANSFER_SERIALIZABLE -> log(TAG, "傳遞過來(lái)的對(duì)象:  ${msg.data?.get("person")}")
                else -> super.handleMessage(msg)
            }
        }

        private fun replyToClient(msg: Message, replyText: String) {
            val clientMessenger = msg.replyTo
            val replyMessage = Message.obtain(null, MSG_FROM_SERVICE)
            replyMessage.data = Bundle().apply {
                putString("reply", replyText)
            }
            try {
                clientMessenger?.send(replyMessage)
            } catch (e: RemoteException) {
                e.printStackTrace()
            }
        }
    }

而客戶端這邊需要做出響應(yīng),則還需在客戶端創(chuàng)建一個(gè)Messenger,并為其創(chuàng)建一個(gè)Handler用于接收服務(wù)端傳遞過來(lái)的消息.在客戶端發(fā)送消息時(shí),需要將Message#replyTo設(shè)置為客戶端的Messenger. 服務(wù)端拿到這個(gè)Messanger才能回復(fù)消息.


/** 客戶端這邊的Messenger */
private var mClientMessenger = Messenger(IncomingHandler())

class IncomingHandler : Handler(Looper.getMainLooper()) {
    override fun handleMessage(msg: Message) {
        when (msg.what) {
            MSG_FROM_SERVICE -> {
                log(TAG, "Received from service: ${msg.data?.getString("reply")}")
            }
            else -> super.handleMessage(msg)
        }
    }
}

private fun sayHello() {
    if (!bound) {
        return
    }
    //創(chuàng)建,并且發(fā)送一個(gè)message給服務(wù)端   Message中what指定為MSG_SAY_HELLO
    val message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
    //注意 這里是新增的
    message.replyTo = mClientMessenger
    message.data = Bundle().apply {
        putSerializable("person", SerializablePerson("張三"))
    }
    try {
        mService?.send(message)
    } catch (e: RemoteException) {
        e.printStackTrace()
    }
}

服務(wù)端調(diào)用sayHello()之后,輸出日志如下:

2020-12-31 11:59:40.420 29702-29702/com.xfhy.allinone D/xfhy_messenger: hello!
2020-12-31 11:59:40.421 29649-29649/com.xfhy.allinone D/xfhy_messenger: Received from service: I have received your message and will reply to you later

日志里面明顯看到是2個(gè)進(jìn)程,所以現(xiàn)在是達(dá)到是雙向通信的目的.Messenger的使用大概就是這些了,下面是Messenger的大致工作原理圖

//todo xfhy 插圖 Messenger的工作原理 Android開發(fā)藝術(shù)探索(P93)

3. 原理

3.1 客戶端->服務(wù)端通信

服務(wù)端

當(dāng)客戶端到服務(wù)端單向通信時(shí),我們來(lái)看一下大致的原理.首先是服務(wù)端這邊在onBind方法中返回了Messenger的binder對(duì)象

override fun onBind(intent: Intent): IBinder {
    //傳入Handler實(shí)例化Messenger
    mMessenger = Messenger(IncomingHandler())
    //將Messenger中的binder返回給客戶端,讓它可以遠(yuǎn)程調(diào)用
    return mMessenger.binder
}

我們看下Messenger里面的binder是什么:

private final IMessenger mTarget;

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

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

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

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

從Messenger的構(gòu)造方法(IMessenger.Stub.asInterface())可以看出它底層應(yīng)該是使用的AIDL搞的.getBinder()其實(shí)是將調(diào)用了mTarget.asBinder(),而mTarget是我們傳進(jìn)來(lái)的Handler里面拿出來(lái)的,跟進(jìn)Handler.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);
    }
}

原來(lái)IMessenger是Handler的內(nèi)部類MessengerImpl,它只有一個(gè)send方法.結(jié)合上面Messenger的源碼,我們發(fā)現(xiàn)調(diào)用Messenger的send方法其實(shí)就是調(diào)用這里的MessengerImpl的send方法,然后這個(gè)send里面將Message轉(zhuǎn)發(fā)給Handler#sendMessage(),最后也就是去了Handler#handleMessage()里面接收到這個(gè)Message.

MessengerImpl是繼承自IMessenger.Stub,這一看就感覺是AIDL文件自動(dòng)生成的嘛,easy.大膽猜測(cè)一下對(duì)應(yīng)的aidl文件應(yīng)該是IMessenger.aidl,我們?nèi)ピ创a里面找IMessenger.aidl,果然在frameworks/base/core/java/android/os/IMessenger.aidl這個(gè)位置找到了它.內(nèi)容如下:

package android.os;

import android.os.Message;

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

根據(jù)aidl文件,它自動(dòng)生成的IMessenger.java應(yīng)該長(zhǎng)下面這樣:

package android.os;

public interface IMessenger extends android.os.IInterface {
    /**
     * Local-side IPC implementation stub class.
     */
    public static abstract class Stub extends android.os.Binder implements IMessenger {
        private static final java.lang.String DESCRIPTOR = "android.os.IMessenger";

        /**
         * Construct the stub at attach it to the interface.
         */
        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        /**
         * Cast an IBinder object into an android.os.IMessenger interface,
         * generating a proxy if needed.
         */
        public static IMessenger asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof IMessenger))) {
                return ((IMessenger) iin);
            }
            return new IMessenger.Stub.Proxy(obj);
        }

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

        @Override
        public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
            java.lang.String descriptor = DESCRIPTOR;
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(descriptor);
                    return true;
                }
                case TRANSACTION_send: {
                    data.enforceInterface(descriptor);
                    android.os.Message _arg0;
                    if ((0 != data.readInt())) {
                        _arg0 = android.os.Message.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.send(_arg0);
                    return true;
                }
                default: {
                    return super.onTransact(code, data, reply, flags);
                }
            }
        }

        private static class Proxy implements IMessenger {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

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

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            @Override
            public void send(android.os.Message msg) throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((msg != null)) {
                        _data.writeInt(1);
                        msg.writeToParcel(_data, 0);
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_send, _data, null, android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void send(android.os.Message msg) throws android.os.RemoteException;
}

這就好辦了,這就明擺著說(shuō)明Messenger底層是基于AIDL實(shí)現(xiàn)的.服務(wù)端這邊這條線: Service#onBind()->mMessenger.getBinder()->Handler#getIMessenger()->MessengerImpl(IMessenger.Stub),其實(shí)就是和我們使用AIDL一樣將IXXX.Stub的子類通過onBind返回回去,客戶端綁定的時(shí)候好拿到binder對(duì)象.接收客戶端的消息時(shí),是通過MessengerImpl轉(zhuǎn)發(fā)給Handler來(lái)完成的,服務(wù)端這邊定義的那個(gè)Handler就可以在handleMessage()中處理跨進(jìn)程傳遞過來(lái)的Message,從而理解客戶端想要調(diào)用什么服務(wù),然后執(zhí)行相應(yīng)的邏輯.

客戶端

再看客戶端這邊,在onServiceConnected()時(shí),將服務(wù)端返回的IBinder對(duì)象放進(jìn)Messenger里.

//MessengerActivity.kt
private val mServiceConnection = object : ServiceConnection {

    override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
        mService = Messenger(service)
        bound = true
    }

    override fun onServiceDisconnected(name: ComponentName?) {
        mService = null
        bound = false
    }
}

//Messenger.java
public void send(Message message) throws RemoteException {
    mTarget.send(message);
}
public Messenger(IBinder target) {
            //這里asInterface 出來(lái)的其實(shí)就是 IMessenger.Stub.Proxy對(duì)象
    mTarget = IMessenger.Stub.asInterface(target);
}

IBinder對(duì)象放進(jìn)Messenger原來(lái)就是熟悉的操作IMessenger.Stub.asInterface(),簡(jiǎn)單.然后客戶端這邊給服務(wù)端發(fā)消息的時(shí)候通過構(gòu)建出來(lái)的Messenger調(diào)用send方法發(fā)送,而Messenger內(nèi)部send的實(shí)現(xiàn)其實(shí)就是調(diào)用IMessenger.Stub.Proxy(跨進(jìn)程了)的send方法.調(diào)用之后,服務(wù)端那邊在Handler的handleMessage里收到這條消息(Message),從而實(shí)現(xiàn)了跨進(jìn)程通信.

3.2 服務(wù)端->客戶端通信

客戶端與服務(wù)端的通信與我們用AIDL的方式實(shí)現(xiàn)幾乎一致,完全可以我們自己實(shí)現(xiàn),Messenger只是幫我們封裝好了而已.下面來(lái)看一下服務(wù)端與客戶端的通信.

服務(wù)端需要與客戶端通信的話,需要客戶端在send消息的時(shí)候?qū)⒖蛻舳薓essenger存放在消息的replyTo中.

private fun sayHello() {
    val message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
    //將客戶方的Messenger放replyTo里
    message.replyTo = mClientMessenger
    mService?.send(message)
}

將消息發(fā)送到服務(wù)端時(shí),因?yàn)槭强邕M(jìn)程,所以肯定需要用到序列化與反序列化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();
    workSourceUid = source.readInt();
}

主要是看一下replyTo是怎么反序列化的,它調(diào)用了Messenger的readMessengerOrNullFromParcel方法:

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

writeMessengerOrNullToParcel中將客戶端的messenger.mTarget.asBinder()進(jìn)行了寫入,然后在readMessengerOrNullFromParcel時(shí)進(jìn)行了恢復(fù),而messenger.mTarget就是上面分析的MessengerImpl,asBinder()是其父類IMessenger.Stub里面的一個(gè)方法:

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

就是將自身返回出去.也就是說(shuō),服務(wù)端反序列化出來(lái)的replyTo對(duì)應(yīng)Messenger中的IBinder其實(shí)就是客戶端的MessengerImpl對(duì)象.于是服務(wù)端拿到這個(gè)Messenger就可以發(fā)送消息,通過這個(gè)IBinder對(duì)象跨進(jìn)程通信,客戶端就接收到消息了.

4. 小結(jié)

跨進(jìn)程通信時(shí),Messenger比AIDL更常用(滿足使用條件的時(shí)候),因?yàn)橛闷饋?lái)比較方便,而且官方也更推薦.在使用Messenger的同時(shí),我們需要了解其原理:

  • 客戶端與服務(wù)端單向通信時(shí),利用的是AIDL接口的原理,和我們平時(shí)寫的方式一樣
  • 服務(wù)端與客戶端通信時(shí),利用客戶端發(fā)送消息時(shí)Message對(duì)象需要序列化與反序列化,將客戶端的binder對(duì)象封裝在里面的replyTo字段中,服務(wù)端那邊反序列化時(shí)再將其取出組裝成Messenger.有了這個(gè)客戶端的binder對(duì)象,當(dāng)然也就能夠與客戶端進(jìn)行跨進(jìn)程通信了.

資料

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容