Android的Messenger

  • 内容
  • 评论
  • 相关

一. Messenger是什么

Messenger是一种轻量级的IPC方案并对AIDL 进行了封装,它实现起来比较容易。
下面Messenger的工作原理图,以便于更好的理解Messenger:
 
Messenger内部消息处理使用Handler实现的,所以它是以串行的方式处理客服端发送过来的消息的,如果有大量的消息发送给服务器端,服务器端只能一个一个处理,如果并发量大的话用Messenger就不合适了,而且Messenger的主要作用就是为了传递消息,很多时候我们需要跨进程调用服务器端的方法,这种需求Messenger就无法做到了。

 二. Messenger的原理及使用

Messenger大致的原理是这样的,因为Messenger的底层还是AIDL,所以,原理和AIDL差不多。

Server:首先需要在服务器创建Binder对象,如何创建呢?通过Messenger来创建,所以我们需要先构造Messenger对象,对于Messenger的构造方法有两种,如下:

1
2
3
4
5
6
7
 public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}
 
 public Messenger(Handler target) {
    mTarget = target.getIMessenger();
}

所以我们需要先构造一个Handler,这个Handler的作用其实就是处理消息。然后我们再通过这个Handler来构造Messenger对象,这个Messenger对象其实就是将客户端发送来的消息传递给Handler来处理,然后我们需要得到Binder对象,通过在Service的onBind方法中return Messenger.getBinder(),这样就得到了Binder对象。

Binder驱动:跟AIDL一样,还是Service。

Client:也是需要得到服务端的Binder对象在binder驱动层对应的mRemote引用,获得的方式是将ServiceConnection中的IBinder service当做参数传入Messenger的构造函数中,如:

1
Messenger mService = new Messenger(service)

然后就可以用mService.send(msg)给服务器发消息。实现跨进程通信。
因为这里是借助Messenger,所以无法调用服务器端的方法,只能通过message来传递消息。而当服务器需要回应客户端的时候,就需要客户端提供一个Messenger,然后服务器得到这个Messenger,因为在就像客户端向服务端发送请求的时候,也是服务器提供一个Messenger,然后客户端得到这个Messenger。那么如何实现呢?因为客户端和服务器已经建立了连接,所以只需要在客户端发送消息的时候,通过消息的replyTo参数向服务器传入一个Messenger,然后服务器在接收到客户端的消息的时候得到通过message的replyTo参数得到这个Messenger,然后利用这个向客户端发送消息就可以了。主要代码如下:
在客户端发送消息给服务器的时候:

1
message.replyTo=clientMessenger;

服务器接收消息的时候:

1
 Messenger clientMessenger=msg.replyTo;

这样就在服务器端得到了客户端的Messenger,然后在服务器端通过clientMessenger.send(message);就向客户端发送了消息。

三. Bundle、Messenger和Message之间的联系

上面说到了Bundle、Messenger、Message这三个类,三个都实现了Parcelable接口,三个同时用于进程间通信,那么这三者有什么联系吗?
其实根据每一个类的构造方法以及主要函数,我们便可以知道这三者的联系了。现在我们把Messenger比喻为一个信使,信使的作用是派信;那么Message就比喻为信件、信封,即信使派的东西;那么Bundle是什么呢?Message里面保存了Bundle,那么bundle可以比喻为信纸,信纸上写满了各种我们要传递的信息。读到这里,读者应该明白了这三者在Messenger通讯方式内所扮演的角色了。

简单来说:Messenger把装有Bundle的Message发送到别的进程

四. 重点

  1. 对于使用Messenger而言,底层其实是AIDL,但是没有AIDL灵活,因为这是借助Messenger来发送消息从而进行消息的传递,不能直接调用服务端的方法,而使用AIDL是直接可以调用服务端的方法。
  2. 对于服务端的Messenger的作用是将客户端传递的消息传递给Handler来处理,而客户端的是发送消息给服务端。
  3. Messenger是以串行的方式处理客户端发来的消息,当消息多的时候就就不合适了。而AIDL是可以并发处理客户端传来的消息。

 五. Messenger的实现代码

1.创建一个Service模拟Server进程

一般的进程间通信多是在两个App之间,但一个App中也可以有多进程,这个很常见,如应用中的推送服务一般位于单独的进程。当然我们可以把这个Service创建到另一个App中,但为了方便测试,这里只是将该Service注册为另一个进程,但还是在同一个应用中。该Service的实现很简单,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
public class MessengerService extends Service {
    private static final String TAG = MessengerService.class.getSimpleName();
    public static final int MSG_CLIENT_TO_SERVER = 35;
    public static final int MSG_SERVER_TO_CLIENT = 36;
    private Messenger mMessenger;
    private WorkThread mWorkThread;
 
    @Override
    public IBinder onBind(Intent intent) {
        return mMessenger.getBinder();
    }
 
    @Override
    public void onCreate() {
        super.onCreate();
        mWorkThread = new WorkThread();
        mWorkThread.start();
        Log.i(TAG,"Server start -------");
    }
 
    @Override
    public void onDestroy() {
        super.onDestroy();
        if (mWorkThread != null)mWorkThread.quit();
    }
 
    private class WorkThread extends Thread {
        MessengerHandler mHandler;
 
        @Override
        public void run() {
            Looper.prepare();
            mHandler = new MessengerHandler();
            prepareMessenger();
            Looper.loop();
        }
 
        public void quit() {
            mHandler.getLooper().quit();
        }
 
        private void prepareMessenger() {
            mMessenger = new Messenger(mWorkThread.mHandler);
        }
    }
 
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MSG_CLIENT_TO_SERVER:
                    Log.i(TAG,"Obtain Client Message-------" + msg.getData().get("msg"));
                    Messenger clientMessenger = msg.replyTo;
                    if (clientMessenger != null) {
                        try {
                            Message message=Message.obtain(null, MSG_SERVER_TO_CLIENT);
                            Bundle bundle=new Bundle();
                            bundle.putString("reply","I am Server,We received the message");
                            message.setData(bundle);
                            clientMessenger.send(message);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                    }
                    break;
                default:
                    break;
            }
        }
    }
}

上述代码虽然简单,但有几点需要注意:

  1. 为什么Service中要开一个工作线程?因为Service作为四大组件之一,它是运行在主线程的,所以不能执行耗时操作,一旦进程间交互是耗时操作,那么Service所在进程就会阻塞,而Client端进程则不会阻塞。
  2. 该Service中创建了一个Messenger对象,并在onBind中返回了IBinder对象,这里是进程间通信的关键,在后面会详细分析。
  3. 该Service的子线程中创建了一个Handler,并关联给Messenger,用于进程间通信的消息处理。Handler消息处理跟我们平时用的一样,但有一点提一下,子线程是没有默认Looper的,因此需要自己创建并启动,否则子线程的Handler无法收到Message。
  4. Server端收到消息后,Toast一下“hello server”并显示Cient传过来的两个整数值。如果Client端也将自己的Messenger传过来了,则向Client端回复消息,将两个整数之和返回。

另外该Service在AndroidManifest.xml中的注册如下:

<service
    android:name=".MessengerService"
    android:enabled="true"
    android:exported="true"
    android:process=":remote">
</service>
 核心一句为android:process=":remote",将该Service置于另一个进程之中,从而可以在同一个App中模拟进程间通信。

2. 创建一个Activity模拟Client进程

该Activity默认就是该App所在进程了,具体实现如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
 public class MessengerActivity extends AppCompatActivity
        implements ServiceConnection, View.OnClickListener {
 
    private static final String TAG = MessengerActivity.class.getSimpleName();
    private boolean mBound = false;
    private Messenger mMessenger;
 
    private Button mBtnStart;
    private Button mBtnBind;
    private Button mBtnSend;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_messenger);
        initView();
    }
 
    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(this);
    }
 
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        mMessenger = new Messenger(service);
        mBound = true;
    }
 
    @Override
    public void onServiceDisconnected(ComponentName name) {
        mMessenger = null;
        mBound = false;
    }
 
    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btnStart:
                startRemoteService();
                mBtnStart.setEnabled(false);
                break;
            case R.id.btnBind:
                bindRemoteService();
                mBtnBind.setEnabled(false);
                break;
            case R.id.btnSend:
                sendRemoteService();
                break;
        }
    }
 
    private void initView(){
        mBtnStart = findViewById(R.id.btnStart);
        mBtnBind = findViewById(R.id.btnBind);
        mBtnSend = findViewById(R.id.btnSend);
 
        mBtnStart.setOnClickListener(this);
        mBtnBind.setOnClickListener(this);
        mBtnSend.setOnClickListener(this);
    }
 
    public void startRemoteService() {
        Intent intent = new Intent();
        intent.setClass(this, MessengerService.class);
        intent.setPackage(this.getPackageName());
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startService(intent);
    }
 
    public void bindRemoteService() {
        Intent intent = new Intent();
        intent.setClass(this, MessengerService.class);
        intent.setPackage(this.getPackageName());
        bindService(intent, this, BIND_AUTO_CREATE);
    }
 
    public void sendRemoteService() {
        if (!mBound){
            Toast.makeText(MessengerActivity.this, "Service not bind", Toast.LENGTH_SHORT).show();
        }
        try {
            Message message = Message.obtain(null, MessengerService.MSG_CLIENT_TO_SERVER);
            Bundle bundle=new Bundle();
            bundle.putString("msg", "I am Client,Obtained server side.");
            message.setData(bundle);
            message.replyTo = new Messenger(new ClientHandler());
            mMessenger.send(message);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
 
    }
 
    private static class ClientHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case MessengerService.MSG_SERVER_TO_CLIENT:
                    Log.i(TAG,"Obtain Server message-------" + msg.getData().get("reply"));
                    break;
                default:
                    break;
            }
        }
    }
 
}

代码逻辑也很简单,界面有3个按钮,操作如下:

  1. 先启动Server端的Service,暂且叫做启动远程Service
  2. 绑定远程Service
  3. Client向Servcie端发送消息,并接收返回的消息

需要注意的有如下几点:

  1. 绑定远程Service后,Client端才拿到了Server端的Messenger引用。
  2. Client端的Messenger需要关联自己的Handler,用来处理从Server端收到的消息。这里也需要注意,理论上如果Server端与Client端交互也是耗时的话,也需要开子线程,这个例子中由于只是显示下消息,直接放在UI线程了。
  3. 如果需要双向通信,Client端需要通过Message的replyTo参数将自己的Messenger发到Server端。

3. 示例效果演示

以上示例的进程间通信效果演示如下:

Server:

 Client:
  相关: