Android IPC 通信之 Messenger

說到 IPC 除了我們之前講的 AIDL ,經(jīng)常被提及的還有 Messenger ,Messenger 底層是通過 AIDL 來實現(xiàn)的。

其實我第一眼看到這個的時候,心想,Messenger ? Message ? Handler ?

確實,跟他們都有一腿。

再說回來,一般越是上層的封裝,越是能簡化操作,Messenger 就是為了簡化 AIDL 的操作成本的。

而從構(gòu)造方法上,我們也可以很明顯的看出 AIDL 的痕跡。

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

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

先說一下使用 Messenger 通信的大致流程吧。

服務(wù)端

首先還是要一個 Service ,用來連接,然后創(chuàng)建一個 Handler ,然后在 Handler 的 handleMessage 中處理具體的業(yè)務(wù)邏輯,再通過這個 Handler 創(chuàng)建 Messenger ,最后在 onBind 中返回 Messenger.getBinder()。

如果想要在收到客戶端的消息后再回復(fù)一條消息給客戶端,可以通過 Message.replyTo 獲取到一個 Messenger ,然后用這個 Messenger 發(fā)消息就可以了。

客戶端

客戶端通過跟 Service 綁定 ,獲取到 IBinder,然后通過 IBinder 創(chuàng)建一個 Messenger ,后面就可以通過 Messenger.send(Message) 發(fā)消息了。

如果想要接收服務(wù)端的返回消息,我們還需要一個 Handler ,使用 這個Handler 創(chuàng)建一個 Messenger 。然后在發(fā)送消息的時候,把這個 Messenger 賦值給 Message.replyTo 。這樣就可以了。

好了,流程知道了,上 Demo !

在這個 Demo ,我們使用的是同一個 APP ,然后把 Service 放到另一個線程。進行跨進程通信。

MessengerService

public class MessengerService extends Service {
    private static final String TAG = "MessengerService";

    private static final int MSG_FROM_CLIENT = 10011;
    private static final int MSG_FROM_SERVICE = 10012;

    public MessengerService() {
    }

    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_FROM_CLIENT:
                    Log.d(TAG, "receive msg from Client: " + msg.getData().getString("msg"));
                    Messenger client = msg.replyTo;
                    Message replyMessage = Message.obtain(null, MSG_FROM_SERVICE);
                    Bundle bundle = new Bundle();
                    bundle.putString("reply","ok,我收到消息了,稍后回復(fù)你。");
                    replyMessage.setData(bundle);
                    try {
                        client.send(replyMessage);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    private final Messenger messenger = new Messenger(new MessengerHandler());
    @Override
    public IBinder onBind(Intent intent){
        return messenger.getBinder();
    }
}

現(xiàn)在看起來,比我們之前寫 AIDL 的時候清晰了許多,我們只需要一個 Handler 來處理具體的事務(wù),然后用這個 Handler 創(chuàng)建 Messenger ,再把 Messenger.getBinder() 返回就可以了。

記得要注冊 Service 。

<service
    android:name=".MessengerService"
    android:process=":remote" />

由于這個 Demo 是在同一個 APP 中進行的,所以可以不用設(shè)置 action ,因為在同一個 APP 中,可以直接通過包名全路徑進行綁定服務(wù)。

客戶端

Main2Activity

public class Main2Activity extends AppCompatActivity {

    private static final String TAG = "MessengerActivity";

    private static final int MSG_FROM_CLIENT = 10011;
    private static final int MSG_FROM_SERVICE = 10012;

    private Messenger mService;

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            mService = new Messenger(iBinder);
            Message msg = Message.obtain(null, MSG_FROM_CLIENT);
            Bundle bundle = new Bundle();
            bundle.putString("msg", "hello ,this is clint");
            msg.setData(bundle);
            msg.replyTo = mGetReplyMessenger;
            try {
                mService.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {

        }
    };

    private Messenger mGetReplyMessenger = new Messenger(new MessengerHandler());

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

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

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

客戶端這邊也很簡單,直接綁定 Service 后,就直接用 IBinder 創(chuàng)建一個 Messenger 來發(fā)消息。為了能接受到服務(wù)端的消息,還創(chuàng)建了一個 Handler 和 Messenger 來接收和處理 服務(wù)端的消息。

分析

這個部分就有意思了,雖然有點繞,但是看懂了,就有一種好像原本一個塞住的地方被捅開的快感。

其實 Messenger 的源碼很簡單,只有一百五十行,去掉大部分的注釋,估計也就幾十行吧。我們還是先從 Messenger 的構(gòu)造方法開始吧。

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

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

Messenger 有兩個構(gòu)造函數(shù),一個是通過 Handler 來創(chuàng)建,一個是通過 IBinder 來創(chuàng)建。

我們先看一下傳 IBinder 參數(shù)的構(gòu)造方法,IMessenger.Stub.asInterface 這個方法是不是很熟悉? 沒錯就是我們之前 AIDL 的寫法,我們通過這個方法獲取到代理對象 Proxy ?,F(xiàn)在構(gòu)造方法把這個代理對象賦值給了 mTarget 這個變量。

mTarget 這個變量定義的時候是一個 IMessenger 類型的變量。之前我們分析 AIDL 的時候,知道 Stub 和 Proxy 都實現(xiàn)了 AIDL 的接口。

我們再看一下 Messenger 發(fā)送消息的時候調(diào)用的方法 send(msg)

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

看到這里,我想你大概應(yīng)該就明白了,這個方法就是使用了 Proxy 代理對象 發(fā)送了一個請求。

其實從這里我們也可以推斷出 Messenger 使用的 AIDL 接口是 send(message)

Messenger 使用的是下面這個 AIDL 文件。

package android.os;
 
import android.os.Message;
 
/** @hide */
oneway interface IMessenger {
    void send(in Message msg);
}

那我們就理解了,Messenger 底層也是通過 AIDL 實現(xiàn)的。跟我們之前學(xué) AIDL 的時候一樣,也是通過 Proxy 代理對象發(fā)送 IPC 請求。

嗯,現(xiàn)在我們知道 Messenger 是怎么發(fā)送消息的了,那怎么接受消息呢?

我們從另一個構(gòu)造方法入手。

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

這里通過 handler.getIMessenger() 獲取到一個 IMessenger 。這個是怎么獲取到的呢?

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

如果已經(jīng)創(chuàng)建了 mMessenger 了,就直接返回,如果沒有就創(chuàng)建一個。

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

看到這,我想你又明白了吧,這就是一個 Stub ,我們知道 Stub 也抽象實現(xiàn)了 AIDL 接口,真正實現(xiàn)是在服務(wù)端。在這里就已經(jīng)實現(xiàn)了,然后實現(xiàn)的方式是通過 Handler 發(fā)送消息。其實我第一次看到這里是時候,心想,Handler還能跨進程通信?其實不是這樣的,我看混了。流程走到這里,已經(jīng)跨過進程了,現(xiàn)在就只是在進程內(nèi)使用 Handler 發(fā)了個消息。

然后這個消息一發(fā),會發(fā)到哪里? 就是發(fā)送到我們創(chuàng)建 Messenger 時候使用的 Handler 。然后我們就可以在 Handler 中處理具體的事務(wù)了。

最后在 Service 的 onBind 中調(diào)用 Messenger.getBinder() 返回 Binder 就可以了。

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

這個就更加明顯了,實際上相當(dāng)于 Stub.asBinder() 返回的是 Stub 本身。

哦! 原來是這樣子。

然后我們在看一下服務(wù)端接收到消息后是怎么回復(fù)消息的。

我們先在 客戶端創(chuàng)建了一個 Handler ,然后使用這個 Handler 創(chuàng)建了一個 Messenger 。最后把這個 Messenger 賦值給 Message.replyTo 這個變量。

服務(wù)端要回復(fù)的時候就直接拿到這個 Messenger ,然后使用 Messenger.send(msg) 就能發(fā)送消息到客戶端了。

從我們上面的分析,可以知道,Messenger 的 send 方法調(diào)用的是 Handler 內(nèi)部繼承了 Stub 的內(nèi)部類的 send 方法,然后這個方法具體操作是 使用這個 Handler 發(fā)送一個消息。

上面的說法是錯誤的,這就是像我一開始說的以為 Handler 能夠跨進程通信。Handler 不能跨進程通信,而且不同的進程是無法互調(diào)的,而是通過 Messenger 發(fā)送 IPC 消息,然后 客戶端的 Stub 接收到消息,然后使用 Handler 發(fā)了個消息。

在這個中間,我有些困惑,通過 Handler 創(chuàng)建的 Messenger ,Messenger 里的 mTarget 其實是 Stub ,而不是 Proxy ,而 Stub 是沒有發(fā)消息的能力的,實現(xiàn)的 IMessenger 也只是接受到消息后進行處理的方法。我就懵逼了,那這個消息是怎么發(fā)送的呢?

我一開始也是想不通,還在群里發(fā)了紅包,求大佬解惑,結(jié)果,有點答非所問,不知道是不是我沒有說清楚問題。。。。

后面 debug 的時候發(fā)現(xiàn),在消息發(fā)過去之前 mTarget 對象還是 IMessenger 類型,但是到接收的時候就變成 Proxy 了,我就大概知道,應(yīng)該是傳輸?shù)臅r候做了什么處理。

然后翻了翻 Message 和 Messenger 的源碼,最后發(fā)現(xiàn),原來是在 Message 在序列化的時候,做了處理。

具體的操作是 Message 在序列化的時候,就傳了個 IBinder ,然后接收后進行反序列化的時候,就直接拿這個 IBinder 創(chuàng)建了一個 Messenger ,這樣在接收的時候,新創(chuàng)建出來的時候,Messenger 里面的 mTarget 就是 Proxy 了,就可以發(fā)消息了。

Message 序列化 和 反序列化 的時候調(diào)用的是 Messenger 的靜態(tài)方法。

Message 序列化 replyTo
Messenger 序列化方法
Messenger 反序列化方法

就這樣,服務(wù)端拿到 Messenger ,就可以向客戶端發(fā)消息了。

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

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

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