使用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

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