Android 進程間通信(四) - Messenger 以及源碼分析

在上幾篇文章中,我們已經學習了Binder 這種通信模式,這章中,我們來學習 Messenger;

Messenger可以翻譯爲信使,它可以在不同進程間傳遞 Message 對象。Messenger 是一種輕量的 IPC 防範,它的底層實現是 AIDL,後面我們會分析它的源碼;

Messenger 一次只能處理一個請求,因此並不適合高併發的問題,也不用考慮線程同步的問題,因爲服務端中不存在併發執行的情形。

一. 實現Messenger通信

從上面可以知道,可以傳遞 Message,一些簡單的數據,我們就可以通過這種方式去實現了。下面我們一起來實現它。
今天的效果是,客戶端發送兩個數字,服務端返回兩數之和:
在這裏插入圖片描述

1.1. 服務端

在服務端中,我們可以直接 new 一個 Messenger ,傳遞一個 Handler,如下:

public class MessengerService extends Service {
    private static final String TAG = "MessageService";
    public MessengerService() {
    }

    Messenger mMessenger = new Messenger(new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
        }
    });

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

可以看到,Messenger 的構造方法中,傳遞了一個 Handler,當接收到客戶端的 Message 消息時,會在 handleMessage() 方法中處理,並通過 mMessenger.getBinder() 返回 binder 對象。

接着,需要通過 Message 拿到兩個數字,並返回相加的結果,如下:

        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            try {
                int num = msg.arg1 + msg.arg2;
                Message message = Message.obtain();
                message.what = 2;
                message.arg1 = num;
                //通過客戶端的 Messenger 來把數據發送回去
                msg.replyTo.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

需要注意的是,數據在返回的時候,需要通過 msg.replyTo 去發送,它的對象爲 Messenger 。
這樣,服務端就寫好了。

1.2. 客戶端

接着,我們新建一個moudle,通過包名加類名的方式去 bind 這個服務。如下:

Intent intent = new Intent();
//綁定 服務
intent.setClassName("com.example.ipcdemo","com.example.ipcdemo.service.MessengerService");
mRemoteService = new RemoteService();
bindService(intent,mRemoteService, Service.BIND_AUTO_CREATE);

在連接的時候,創建 Messenger :

    class RemoteService implements ServiceConnection {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mBinder = new Messenger(service);
            mSb.append("連接上服務端").append("\n");
            mTextView.setText(mSb.toString());
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mBinder = null;
        }
    }

這樣,我們就拿到了服務端mBinder的實例了,然後通過點擊事件,把數據發送過去:

    public void testBtn(View view) {
        Message msg = Message.obtain();
        msg.arg1 = (int) (Math.random() * 100+1);
        msg.arg2 = (int) (Math.random() * 100+1);
        mTextView.setText(mSb.toString());
        //傳遞 client 的 messenger ,實現服務端與客戶端通信
        msg.replyTo = mMessenger;
        if (mBinder != null) {
            try {
                mBinder.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    }

同時,也要注意,把要接受服務端信息的 Messenger 綁定到 msg.replyTo 中。客戶端的 Messenger 如下:

    Messenger mMessenger = new Messenger(new Handler(Looper.getMainLooper()){
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            mSb.append(msg.arg1).append("\n");
            mTextView.setText(mSb.toString());
        }
    });

這樣,我們就實現所有功能了。效果如下面的 gif 。

使用 Messenger 的優缺點如下:

優點 缺點 使用場景
功能一般,支持一對多穿行通信,支持實時通信 不能很好的處理高併發情形,不支持RPC,數據通過Message傳輸,因此只能傳輸Bundle支持的數據類型,比如 msg.setData() 低併發的一對多及即時通信,無RPC需求,或者無需要返回結果的RPC需求

二. 源碼分析

上面說到,Messenger 是基於 AIDL 的,怎麼得出來的呢?它的構造方法傳遞要求傳入的 Handler,進入到裏面看一下:

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

然後,去到 Handler ,找到 getIMessage() 方法:
在這裏插入圖片描述
可以看到,mMessenger 的實現類爲 MessengerImpl ,而它繼承自 IMessenger.Stub ;看到這裏有沒有很熟悉? 沒錯,跟我們的 AIDL 的實現類一樣,通過源碼也知道,IMessenger 確實是個 AIDL 接口:

package android.os;

import android.os.Message;

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

在它的 send 方法中,把數據又通過 Handler 的 sendMessage 方法,發送給自身,從而當有數據來的時候,就會調用它的 handleMessage()方法。

而在客戶端的綁定數據中,也驗證了這個問題,比如:
在這裏插入圖片描述
去到構造方法中,可以看到:
在這裏插入圖片描述
通過 asInterface 拿到binder。

這樣,我們就瞭解清楚了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章