使用Messenger實現IPC

前言

在Android中實現IPC(進程間通信)的方式有很多,本文講解如何使用Messenger進行實現。Messenger可以翻譯爲信使,用於跨進程傳輸Message對象。在Message對象中,我們可以附帶其他數據,這就實現了數據的跨進程傳遞。簡而言之,Messenger是一種輕量級的IPC方案,在它的底層封裝了AIDL

支持的數據類型

Messenger只能傳遞Message對象。因此,Message支持的數據類型就是Messenger支持的數據類型。Messagearg1、arg2、what字段各自可以附加一個int型數據,而obj字段可以附加一個Object對象,但是隻支持系統提供的Parcelable對象。因此,傳遞數據的重任就落在了Bundle身上。通過Bundle,我們可以傳遞以下類型的數據:

  • 基本數據類型
  • StringCharSequence
  • 實現Parcelable接口的對象
  • 實現Serializable接口的對象

基本流程

服務端進程

首先我們要創建一個Service,用於提供服務。在Service中,我們需要創建一個Handler對象,用於接收客戶端進程發來的數據。其次,通過這個Handler創建一個Messenger對象。在Messenger對象中封裝有Binder。因此,我們在ServiceonBind方法中將Messenger對應的Binder對象返回。

客戶端進程

首先,我們需要綁定服務器端進程的Service,並通過綁定後返回的IBinder對象創建一個Messenger。藉助這個Messenger對象,我們就可以向服務器端進程發送Message數據了。

雙向通信

通過上述步驟,我們只能做到從客戶端進程向服務端進程傳遞數據,即單向通信。如果同時還要求從服務器端進程向客戶端進程傳遞數據,就需要在客戶端進程也創建一個Handler對象,用於接收服務器端傳來的數據。此外,我們要通過這個Handler創建一個Messenger對象,並通過MessagereplyTo字段傳遞給服務器端進程。服務器端可以從傳來的Message中取出這個Messenger對象,然後就可以使用它向客戶端傳遞數據了。這樣,就實現了客戶端和服務器端間的雙向通信

實現單向通信

服務器端:

public class ServerService extends Service {
    private static final String TAG="ServerService";
    public static final int DATA_FROM_CLIENT=1;

    //自定義Handler,用於接收客戶端傳來的數據
    private static class ServerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case DATA_FROM_CLIENT:
                    Bundle bundle=msg.getData();
                    Log.v(TAG,bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER));
                    break;
                default:
                    break;
            }
        }
    }

    private Messenger messenger=new Messenger(new ServerHandler());

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();//返回Messenger對象底層封裝的Binder
    }
}

可以看到,我們在Service中定義了一個靜態內部類ServerHandler。同時,我們通過以下代碼創建了一個Messenger對象:

private Messenger messenger=new Messenger(new ServerHandler());

onBind方法中,我們通過getBinder方法返回了Messenger封裝的Binder對象。

return messenger.getBinder();//返回Messenger對象底層封裝的Binder

這樣,客戶端傳遞的數據就可以在ServerHandler中處理了。在ServerHandler中,我們通過以下代碼取出Message中的數據:

Bundle bundle=msg.getData();
bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER);

同時,我們爲這個Service指定一個私有進程名,這樣就讓這個Service運行在獨立的進程中了。

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

客戶端:

public class ClientActivity extends AppCompatActivity {
    public static final String KEY_HELLO_TO_SERVER="HELLO_TO_SERVER";

    private Messenger serverMessenger;//來自服務器端的Messenger

    private ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            serverMessenger=new Messenger(service);

            Bundle bundle=new Bundle();
            bundle.putString(KEY_HELLO_TO_SERVER,"你好,服務器~");
            Message message=new Message();
            message.what=ServerService.DATA_FROM_CLIENT;
            message.setData(bundle);
            try {
                serverMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        Intent intent=new Intent(this,ServerService.class);
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);//解綁
    }
}

可以看到,我們通過匿名內部類的方式創建了一個ServiceConnection對象。在它的onServiceConnected方法中,我們通過獲取到的IBinder對象創建了一個Messenger,通過這個Messenger就可以向服務器端發送Message了。

serverMessenger=new Messenger(service);

具體的發送代碼如下:

Bundle bundle=new Bundle();
bundle.putString(KEY_HELLO_TO_SERVER,"你好,服務器~");
Message message=new Message();
message.what=ServerService.DATA_FROM_CLIENT;
message.setData(bundle);
serverMessenger.send(message);

我們先創建了一個Bundle對象,並向其中存儲了一個字符串。隨後,我們創建了一個Message對象,並將前面的Bundle對象設置給Message。最後,通過ServerMessengerMessage發送出去。

需要注意的是,一定要記得在ActivityonDestroy方法中解除對Service的綁定。

unbindService(serviceConnection);//解綁

運行結果:

....../com.example.ipcdemo:remote V/ServerService: 你好,服務器~

實現雙向通信

我們在上面實現了一個單向通信的例子。但是要做到從服務器端給客戶端發送數據,還需要進行修改。主要改動在於客戶端,我們需要提供一個HandlerMessenger。具體改動如下,在主要的新增代碼前都添加了註釋//Add

服務器端:

public class ServerService extends Service {
    private static final String TAG="ServerService";
    public static final int DATA_FROM_CLIENT=1;
    public static final String KEY_HELLO_TO_CLIENT="HELLO_TO_CLIENT";

    //自定義Handler,用於接收客戶端傳來的數據
    private static class ServerHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case DATA_FROM_CLIENT:
                    Bundle bundle=msg.getData();
                    Log.v(TAG,bundle.getString(ClientActivity.KEY_HELLO_TO_SERVER));

                    //Add
                    Messenger clientMessenger=msg.replyTo;
                    Bundle bundleToClient=new Bundle();
                    bundleToClient.putString(KEY_HELLO_TO_CLIENT,
                           "你好,客戶端。我收到你的消息了。");
                    Message message=new Message();
                    message.what=ClientActivity.DATA_FROM_SERVER;
                    message.setData(bundleToClient);

                    try {
                        clientMessenger.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
                default:
                    break;
            }
        }
    }

    private Messenger messenger=new Messenger(new ServerHandler());

    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();//返回Messenger對象底層封裝的Binder
    }

}

可以看到,在服務器端的主要改變是在Handler中從MessagereplyTo參數中獲取了客戶端提供的Messenger對象。通過這個對象我們就可以向客戶端發送Message了。

Messenger clientMessenger=msg.replyTo;

客戶端:

 public class ClientActivity extends AppCompatActivity {
    private static final String TAG="ClientActivity";
    public static final String KEY_HELLO_TO_SERVER="HELLO_TO_SERVER";
    public static final int DATA_FROM_SERVER=0;

    private Messenger serverMessenger;//來自服務器端的Messenger

    private ServiceConnection serviceConnection=new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            serverMessenger=new Messenger(service);

            Bundle bundle=new Bundle();
            bundle.putString(KEY_HELLO_TO_SERVER,"你好,服務器~");
            Message message=new Message();
            message.what=ServerService.DATA_FROM_CLIENT;
            message.setData(bundle);
            //Add
            message.replyTo=messenger;

            try {
                serverMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
        }
    };

    //Add
    private static class ClientHandler extends Handler{
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case DATA_FROM_SERVER:
                    Bundle bundle=msg.getData();
                    Log.v(TAG,bundle.getString(ServerService.KEY_HELLO_TO_CLIENT));
                    break;
                default:
                    break;
            }
        }
    }
    //Add
    private Messenger messenger=new Messenger(new ClientHandler());

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_client);

        Intent intent=new Intent(this,ServerService.class);
        bindService(intent,serviceConnection,BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(serviceConnection);//解綁
    }

}

可以看到,我們定義了一個靜態內部類作爲客戶端的Handler類,並通過Handler創建了一個Messenger對象。這一過程和服務器端的做法一致。在綁定Service時,我們將這個Messenger作爲MessagereplyTo參數傳遞給了服務器端。這樣,服務器端就可以獲取到客戶端提供的Messenger了。具體代碼如下:

message.replyTo=messenger;

運行結果:

....../com.example.ipcdemo:remote V/ServerService: 你好,服務器~
....../com.example.ipcdemo V/ClientActivity: 你好,客戶端。我收到你的消息了。

小結

通過以上例子,可以看出Messenger的使用是很方便的,畢竟系統已經爲我們做了封裝。但是,這種方式的侷限性也很明顯。首先,通過這種方式只能傳遞Message,無法實現遠程方法調用(RPC)。其次,從上文的代碼也可以看出Messenger的消息傳遞是通過Handler進行處理的,這意味着Messenger的消息是串行處理的,顯然這不適合高併發的場景。如果有更高的交互要求,可以考慮使用AIDL實現IPC。具體請參考另一篇博客:

使用AIDL實現IPC

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