Andorid进程间通信(IPC)——Messenger

版权声明:本文出自阿钟的博客,转载请注明出处: https://blog.csdn.net/a_zhon/article/details/81001075

Messenger可以翻译为信使,通过它可以在不同的进程中传递Message对象,在Message中放入我们需要传递的数据,
这样就可以轻松的实现进程间通信了

Android中的 IPC 方式

  • 1.使用Bundle
  • 2.使用文件共享
  • 3.使用Messenger
  • 4.使用AIDL
  • 5.使用ContentProvider
  • 6.使用Socket

IPC的优缺点和适用场景

名称 优点 缺点 使用场景
Bundle 简单易用 只能传输Bundle支持的数据类型 四大组件间的进程间通信
文件共享 简单易用 不适合高并发场景,并且无法做到进程将即时通信 无并发访问情形,交换简单的数据实时性不高的场景
AIDL 功能强大,支持一对多并发通信,支持实时通信 使用稍复杂。需要处理好线程同步 一对多通信且有RPC需求
Messenger 功能一般,支持一对多串行通信,支持实时通信 不能很好处理高并发情形,不支持RPC,数据通过Message进行传输,因此只能传输Bundle支持的数据类型 低并发的一对多通信,无RPC需求,或者无需要返回结果的RPC需求
ContentProvider 在数据资源访问方面功能强大,支持一对多并发数据共享,可通过Call方法扩展其他操作 可以理解为受约束的AIDL,主要提供数据源的CRUD操作 一对多的进程间的数据共享
Socket 功能强大,可以通过网络传输字节流,支持一对多并发实时通信 实现细节稍微有点繁琐,不支持直接的RPC 网络数据交换

实现一个简单的进程间通信,首先创建一个服务端

  • 服务端进程(RemoteService)
public class RemoteService extends Service {
    @SuppressLint("HandlerLeak")
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            String key = msg.getData().getString("key");
            Log.e("TAG",key);
            Toast.makeText(RemoteService.this, key, Toast.LENGTH_SHORT).show();
        }
    };
    private Messenger messenger = new Messenger(handler);
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return messenger.getBinder();
    }
}

AndroidManifest.xml配置这个服务(在一个新的进程中运行)

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

服务端创建一个Handler并通过它来创建一个Messenger对象,然后在Service中onBind函数中返回Messenger对象底层的Binder即可;如果客户端发送消息过来了在handler中就可以接收到

  • 客户端进程
private Messenger messenger;
//绑定远程服务
Intent intent = new Intent(this, RemoteService.class);
bindService(intent, conn, BIND_AUTO_CREATE);

//绑定服务的回调
private ServiceConnection conn = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        messenger = new Messenger(service);
    }
    @Override
    public void onServiceDisconnected(ComponentName name) {
    }
};

客户端绑定Service,绑定成功后通过服务端返回的IBinder对象创建一个Messenger对象,通过这个Messenger就可以像服务端发送消息了。发送的消息类型为Message对象,这里需要注意的是Message中能使用的载体只有what、arg1、arg2、Bundle以及replayTo。

客户端发送消息至服务端

public void sendMsg(View view) {
    Message msg = new Message();
    Bundle bundle = new Bundle();
    bundle.putString("key", "客户端发来了一条消息");
    msg.setData(bundle);
    try {
        messenger.send(msg);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}
  • logcat日志
07-11 05:37:48.317 9715-9715/com.azhon.messenger:remote E/TAG: 客户端发来了一条消息

通过上面几步便可以实现客户端往服务端传输数据了,那现在在来升级下难度,当服务端收到消息时回应客户端 “我收到了消息,一会回复你!”。

首先我们需要在我们的客户端创建一个接收消息的Messenger

 @SuppressLint("HandlerLeak")
 private Handler receiveHandler = new Handler() {
     @Override
     public void handleMessage(Message msg) {
         String key = msg.getData().getString("key");
         Log.e("TAG", key);
     }
 };
 private Messenger receiveMessenger = new Messenger(receiveHandler);

乍一看是不是跟上面写的服务端一致,你没看错就是一样的。接下来重点就是在客户端发送消息的时候我们需要将这个receiveMessenger
传递给服务端(msg.replyTo = receiveMessenger)如下代码:

public void sendMsgReplay(View view) {
    Message msg = new Message();
    Bundle bundle = new Bundle();
    bundle.putString("key", "客户端发来了一条消息");
    msg.setData(bundle);
    //将接收消息的Messenger传递给服务端
    msg.replyTo = receiveMessenger;
    try {
        messenger.send(msg);
    } catch (RemoteException e) {
        e.printStackTrace();
    }
}

客户端已经将接收的Messenger传递给服务端了,那服务端就只要获取到然后在向客户端发送一条消息,这样就达到了
服务端收到消息回复客户端的一个效果。

服务端收到消息回复客户端

 @SuppressLint("HandlerLeak")
 private Handler handler = new Handler() {
     @Override
     public void handleMessage(Message msg) {
         String key = msg.getData().getString("key");
         Log.e("TAG", key);
         Toast.makeText(RemoteService.this, key, Toast.LENGTH_SHORT).show();

         //回复客户端,通过msg.replyTo获取到客户端的Messenger对象
         Messenger reply = msg.replyTo;
         Message message = new Message();
         Bundle bundle = new Bundle();
         bundle.putString("key", "我收到了消息,一会回复你!");
         message.setData(bundle);
         try {
             reply.send(message);
         } catch (RemoteException e) {
             e.printStackTrace();
         }
     }
 };

在服务端收到的Message对象中通过Messenger reply = msg.replyTo获取客户传递过来的Messenger,然后发送消息即可

  • logcat日志
07-11 06:53:55.863 10724-10724/com.azhon.messenger:remote E/TAG: 客户端发来了一条消息
07-11 06:53:55.898 10710-10710/com.azhon.messenger E/TAG: 我收到了消息,一会回复你!

本文示例代码下载

阅读更多

扫码向博主提问

Code-Porter

博客专家

非学,无以致疑;非问,无以广识
  • 擅长领域:
  • Android
  • ReactNativ
  • 微信小程序
去开通我的Chat快问

没有更多推荐了,返回首页