Android進程間通信之Messenger

一、 摘要

本文介紹Android中的IPC方式之一——Messenger。


二、 關於Messenger

SDK中如此描述:

/**
 * Reference to a Handler, which others can use to send messages to it.
 * This allows for the implementation of message-based communication across
 * processes, by creating a Messenger pointing to a Handler in one process,
 * and handing that Messenger to another process.
 *
 * <p>Note: the implementation underneath is just a simple wrapper around
 * a {@link Binder} that is used to perform the communication.  This means
 * semantically you should treat it as such: this class does not impact process
 * lifecycle management (you must be using some higher-level component to tell
 * the system that your process needs to continue running), the connection will
 * break if your process goes away for any reason, etc.</p>
 */

大意是說,引用一個可以發送message的Handler。這允許基於message實現的跨進程通信,通過Handler創建一個Messanger,傳遞message到其他進程。需要注意的是,其實現僅僅是對Binder的簡單包裝。這意味着:Messenger不應該影響進程的生命週期管理(你必須用更高級別的組件來告訴系統你的進程需要繼續執行),一旦你的進程因爲任何原因中斷,Messenger的連接也會斷開。

整理出以下幾個要點:

  • 通過Handler創建一個Messanger
  • Messenger基於Binder實現
  • 注意你的組件的生命週期

查看API文檔,Messenger有兩個構造方法:

private final IMessenger mTarget;

/**
 * Create a new Messenger pointing to the given Handler.  Any Message
 * objects sent through this Messenger will appear in the Handler as if
 * {@link Handler#sendMessage(Message) Handler.sendMessage(Message)} had
 * been called directly.
 * 
 * @param target The Handler that will receive sent messages.
 */
public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

/**
 * Create a Messenger from a raw IBinder, which had previously been
 * retrieved with {@link #getBinder}.
 * 
 * @param target The IBinder this Messenger should communicate with.
 */
public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}

簡單地理解Messanger,顧名思義就是一個信使,客戶端和服務端之間不直接進行消息通信,而是通過信使來傳達消息。


三、 編寫一個Demo

1. 服務端

我們在Service中實現一個Messanger:

public class MessengerDemoService extends Service {
    private final static int MSG_RECEIVE_FROM_CLIENT_0 = 0;
    private final static int MSG_RECEIVE_FROM_CLIENT_1 = 1;
    private final static int MSG_REPLY_TO_CLIENT = 2;

    private final Messenger mMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_RECEIVE_FROM_CLIENT_0:
                    // 獲取客戶端發送的message中的數據
                    Bundle mBundle = msg.getData();
                    // TODO
                    break;
                case MSG_RECEIVE_FROM_CLIENT_1:
                    // 如果我們需要向客戶端返回消息,則像這樣:
                    Message msgReply = Message.obtain();
                    msgReply.what = MSG_REPLY_TO_CLIENT;
                    Bundle bundle = new Bundle();
                    // TODO 在bundle中放入回傳數據
                    msgReply.setData(bundle);
                    try {
                        // msg.replyTo指向回傳客戶端的Messenger實例
                        msg.replyTo.send(msgReply);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
        }
    });

    @Override
    public IBinder onBind(Intent intent) {
        /**
         * 通過返回Messenger實例的binder來和Service進行綁定,
         * 當客戶端向該Service發送message時,便通過binder將消息發送至Messenger實例,
         * 即最終將消息分發到Messenger實例的handleMessage方法中
         */
        return mMessenger.getBinder();
    }
}

然後在AndroidManifest中註冊Service:

<service
    android:name=".MessengerDemoService"
     <!--表示允許其他應用調用我們這個Service-->
    android:exported="true">
</service>

2. 客戶端

在客戶端,我們需要聲明兩個Messenger,一個用來向服務端發送消息,一個用於接收服務端返回的消息。爲什麼在客戶端不能像服務端那樣直接使用replyTo的方式來回傳呢?如果客戶端和服務端都在接收消息後回傳,兩邊可能就會一直傳來傳去,這樣沒完沒了,程序就完了!並且按照C/S架構來講,我們的兩個Messenger分別對應request和response,而服務端收到request可能會進行response,如此描述,也就明白了吧。

我們在Activity中創建這兩個Messenger實例:

public class MessengerDemoClient extends AppCompatActivity {
    private final static int MSG_RECEIVE_FROM_CLIENT_0 = 0;
    private final static int MSG_RECEIVE_FROM_CLIENT_1 = 1;
    private final static int MSG_REPLY_TO_CLIENT = 2;

    // 這是客戶端用於接收服務端返回的Messenger
    private Messenger mClientMessenger = new Messenger(new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_REPLY_TO_CLIENT:
                    // 獲取客戶端返回的message中的數據
                    Bundle mBundle = msg.getData();
                    // TODO
                    break;
                default:
                    break;
            }
        }
    });

    // 這是客戶端用於向服務端發送消息的Messenger
    private Messenger mServerMessenger;

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            /**
             * 這裏用到了Messenger的IBinder構造方式,
             * 通過綁定Service的binder,我們的Messenger實例,
             * 便可以通過該binder向Service發送消息
             */
            mServerMessenger = new Messenger(service);
        }

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

    /**
     * 我們需要在Activity創建時綁定Service,通常在onCreate中
     */
    private void bindService() {
        Intent intent = new Intent();
        // 如果我們給Service添加了用於啓動的filter:
        intent.setAction("啓動的filter");
        // TODO 使用setComponent或者setPackage來指定Service所在的應用
        bindService(intent, mServiceConnection, Context.BIND_AUTO_CREATE);
    }

    private void testSending() {
        Message msgSend = Message.obtain();
        msgSend.what = MSG_RECEIVE_FROM_CLIENT_1;
        Bundle bundle = new Bundle();
        // TODO 在bundle中放入傳給服務端的數據
        msgSend.setData(bundle);
        // 指定我們這次發送消息的返回信息由mClientMessenger來處理
        msgSend.replyTo = mClientMessenger;
        try { 
            mServerMessenger.send(msgSend); 
        } catch (RemoteException e) { 
            e.printStackTrace(); 
        }
    }
}

最後別忘了檢查我們的Activity是否在AndroidManifest中註冊了。


四、 參考文獻

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