天天看點

程序通信之 Messenger 使用與解析

Messenger 簡介

Messenger “信使”,顧名思義,它的作用就是傳遞資訊。

Messenger 有兩個構造函數:

  1. 以 Handler 為參數
  2. 以 Binder 為參數
private final IMessenger mTarget;
public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}
public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);   
}
           

看下 

Handler.getIMessenger()

 源碼:

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

這個 

IMessanger

 應該也是個 AIDL 生成的類吧,看下源碼,果然是:

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

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static android.os.IMessenger asInterface(...}

        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 {...}

        private static class Proxy implements android.os.IMessenger {...}

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

IMessenger

 是 AIDL 生成的跨程序接口,裡面定義了一個發送消息的方法:

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

Handler

 中 

MessengerImpl

 實作了這個方法,就是使用 Handler 将消息發出去:

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

這就解釋了為什麼我們的消息來的時候會出現在 

Handler.handlerMessage()

 中。

接着再看下 Messenger 另一個重要的方法,

send()

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

Messenger

 中持有一個 

IMessenger

 的引用,在構造函數中可以通過 

Handler

 或者 

Binder

 的形式獲得最終的 

IMessenger

 實作,然後調用它的 

send()

 方法。

Messenger

 其實就是 AIDL 的簡化版,它把接口都封裝好,我們隻需在一個程序建立一個 

Handler

 傳遞給 Messenger,Messenger 幫我們把消息跨程序傳遞到另一個程序,我們在另一個程序的 Handler 在處理消息就可以了。

Messenger 的使用

Messenger 的使用需要結合 

Handler

Message

Bundle

 。

下面我們将寫一個用戶端跨程序發送消息到服務端的例子,服務端在收到消息後會回複,由于在 Messenger 中一個對象對應一個 Handler,是以我們需要在用戶端、服務端分别建立一個 Messenger:

程式通信之 Messenger 使用與解析

服務端在收到消息後會使用 

Message.replyTo

 對應的信使回複消息。

服務端

服務端隻需要建立一個 Messenger 對象,然後給它傳遞一個 Handler,在 Handler 中處理消息:

public class MessengerService extends BaseService {

    private final String TAG = this.getClass().getSimpleName();

    Messenger mMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(final Message msg) {
            if (msg != null && msg.arg1 == ConfigHelper.MSG_ID_CLIENT) {
                if (msg.getData() == null) {
                    return;
                }
                String content = (String) msg.getData().get(ConfigHelper.MSG_CONTENT);  //接收用戶端的消息
                LogUtils.d(TAG, "Message from client: " + content);

                //回複消息給用戶端
                Message replyMsg = Message.obtain();
                replyMsg.arg1 = ConfigHelper.MSG_ID_SERVER;
                Bundle bundle = new Bundle();
                bundle.putString(ConfigHelper.MSG_CONTENT, "聽到你的消息了,請說點正經的");
                replyMsg.setData(bundle);

                try {
                    msg.replyTo.send(replyMsg);     //回信
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
            }
        }
    });

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

用戶端

public class IPCTestActivity extends BaseActivity {
    private final String TAG = this.getClass().getSimpleName();

    @BindView(R.id.tv_result)
    TextView mTvResult;
    @BindView(R.id.btn_add_person)
    Button mBtnAddPerson;
    @BindView(R.id.et_msg_content)
    EditText mEtMsgContent;
    @BindView(R.id.btn_send_msg)
    Button mBtnSendMsg;

    /**
     * 用戶端的 Messenger
     */
    Messenger mClientMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(final Message msg) {
            if (msg != null && msg.arg1 == ConfigHelper.MSG_ID_SERVER){
                if (msg.getData() == null){
                    return;
                }

                String content = (String) msg.getData().get(ConfigHelper.MSG_CONTENT);
                LogUtils.d(TAG, "Message from server: " + content);
            }
        }
    });

    //服務端的 Messenger
    private Messenger mServerMessenger;

    private ServiceConnection mMessengerConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(final ComponentName name, final IBinder service) {
            mServerMessenger = new Messenger(service);
        }

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

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_aidl);
        ButterKnife.bind(this);
        bindAIDLService();
        bindMessengerService();
    }

    private void bindMessengerService() {
        Intent intent = new Intent(this, MessengerService.class);
        bindService(intent, mMessengerConnection, BIND_AUTO_CREATE);
    }


    @OnClick(R.id.btn_send_msg)
    public void sendMsg() {
        String msgContent = mEtMsgContent.getText().toString();
        msgContent = TextUtils.isEmpty(msgContent) ? "預設消息" : msgContent;

        Message message = Message.obtain();
        message.arg1 = ConfigHelper.MSG_ID_CLIENT;
        Bundle bundle = new Bundle();
        bundle.putString(ConfigHelper.MSG_CONTENT, msgContent);
        message.setData(bundle);
        message.replyTo = mClientMessenger;     //指定回信人是用戶端定義的

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

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

}
           

使用小結

可以看到用戶端的操作主要有 3 步:

  1. 建立用戶端的 

    Messenger

    ,傳遞一個 

    Handler

     處理消息。
  2. bindService

    ,在 

    ServiceConnection

     回調中拿到服務端的 

    Messenger

  3. 發送消息 
    • Message.obtain()

       消息池裡擷取一個空閑消息對象
    • 使用 

      message.setData(bundle)

       設定資料
    • 指定回信的信使 

      message.replyTo = mClientMessenger

    • 調用服務端信使,發射!

      mServerMessenger.send(message)

總結

Messenger 對 AIDL 進行了封裝,也就是對 Binder 的封裝,我們可以使用它的實作來完成基于消息的跨程序通信,就和使用 Handler 一樣簡單。

使用步驟:

  1. 用戶端建立一個 

    Messenger

    ,傳遞一個 Handler 處理消息
  2. 服務端也一樣

如果需要回信,給 

Message

 設定一個用于回信的 Messenger 即可:

message.replyTo = mClientMessenger;
           

用戶端在調用

send()

方法之後,就會走 Binder 跨程序通信機制 ,最後到服務端的 Handler 中得到處理。