美文网首页Android开发
024 Android多进程-Messenger与Message

024 Android多进程-Messenger与Message

作者: 凤邪摩羯 | 来源:发表于2021-01-13 09:06 被阅读0次

    一、前言

    经过前面两节,我们知道,进程间传递基本数据和序列化数据,可以通过Messager与Message来搭配实现,那么本文来讲一下Messager与Message。

    二、Messenger与Messenge

    Messenger 可以翻译为信使,顾名思义,通过它可以在不同进程中传递Message对象.
    Message 可以翻译为信封,顾名思义,信封里附带着数据

    先看Messenger官方文档的描述:

    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.

    大概意思是说,首先Messenger要与一个Handler相关联,才允许以message为基础的会话进行跨进程通讯。通过创建一个messenger指向一个handler在同一个进程内,然后就可以在另一个进程处理这个messenger了。

    在Message中放入我们需要传递的数据,Messenger就可以轻松地实现在进程间传递数据了。

    Messenger

    Messenger是一种轻量级的IPC方案,它的底层实现是AIDL,为什么这么说呢,我们大致看一下Messenger这个类的构造方法就明白了。

        /**
         * 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);
        }
    
    

    从Messenger两个构造方法的实现上我们可以明显看出AIDL的痕迹,不管是IMessenger还是Stub.asInterface,这种使用方法都表明它的底层是AIDL。

    我们接着来看Messenger的两个重要方法:

    getBinder() : 返回一个IBinder对象,一般在服务端的onBind方法调用这个方法,返回给客户端一个IBinder对象
    send(Message msg) : 发送一个message对象到messengerHandler。这里,我们传递的参数是一个Message对象,

    Messenge

    如果说Messenger充当了信使的角色,那么Message就充当了一个信封的角色。同样地,先看官方文档的描述:

    Defines a message containing a description and arbitrary data object that can be sent to a Handler. This object contains two extra int fields and an extra object field that allow you to not do allocations in many cases.While the constructor of Message is public,the best way to get one of these is to call Message.obtain() or one of the Handler.obtainMessage() methods, which will pull them from a pool of recycled objects.

    从官文的描述可知,该Message对象含有两个Int型的属性和一个object型的属性,然后创建Message的实例,最好调用Message.obtain()方法而不是直接通过构造器。我们来看看主要参数以及重要方法:

    • 属性 public int arg1,public int arg2,public Object obj : 一般这三个属性用于保存数据,其中Object对象用于保存一个对象。
    • 属性 public Messenger replyTo : 这个属性一般用于服务端需要返回消息给客户端的时候用到,下面会说到。
    • 属性 public int what:这个属性用于描述这个message,一般在实例化的时候会传递这个参数。
    • 方法 obtain():提供了多个参数的重载方法,为了获得message实例。
    • setData(Bundle data):设置obj的值,Bundle将在下节单独讲一下。

    三、实现举例

    Messenger的使用方法很简单,它对AIDL做了封装,使得我们可以更简便地进行进程间通信。同时,由于因为Handler的机制一次处理一个请求,因此在服务端我们不用考虑线程同步的问题。实现一个Messenger有如下几个步骤,分为服务端和客户端。

    1. 创建一个Service

    首先,我们需要在服务端创建一个Service来处理客户端的连接请求,同时创建一个Handler并通过它来创建一个Messenger对象,然后在Service的onBind中返回这个Messenger对象底层的Binder即可。
    代码如下:

    public class MessengerService extends Service{
    
        class IncomingHandler extends Handler{
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case 0:
                        Toast.makeText(getApplicationContext(), "hello, trampcr", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        }
    
        Messenger mMessenger = new Messenger(new IncomingHandler());
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
            return mMessenger.getBinder();
        }
    }
    
    

    2. 声明进程

    然后,在AndroidManifest中声明Service并给一个进程名,使该服务成为一个单独的进程。
    AndroidManifest配置如下:

    <service android:name=".MessengerService"  
             android:process="yb.demo.myProcesses.musicservice"/>
    
    

    3. 创建客户端

    户端进程中,首先要绑定服务端的Service,绑定成功后用服务端返回的IBinder对象创建一个Messenger,通过这个Messenger就可以向服务端发送消息了,发消息类型为Message对象。这听起来可能还是有点抽象,不过看了下面的两个例子,读者肯定就都明白了。首先,我们来看一个简单点的例子,在这个例子中服务端无法回应客户端。
    首先看服务端的代码,这是服务端的典型代码,可以看到MessengerHandler用来处理客户端发送的消息,并从消息中取出客户端发来的文本信息。而mMessenger是一个Messenger对象,它和MessengerHandler相关联,并在onBind方法中返回它里面的Binder对象,可以看出,这里Messenger的作用是将客户端发送的消息传递给MessengerHandler处理。

    public class MessengerActivity extends Activity{
    
        private boolean mBound;
        private Messenger mMessenger;
        private ServiceConnection mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mMessenger = new Messenger(service);
                mBound = true;
    
                //不需要接收服务端的回复时,这样发送
                Message msg = Message.obtain(null, 0, 0, 0);
                try {
                    LogUtils.e("sayHello: mMessenger发送消息");
                    mMessenger.send(msg);
                } catch (RemoteException e) {
                    LogUtils.e("sayHello: " + e.toString());
    
                    e.printStackTrace();
                }
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mMessenger = null;
                mBound = false;
            }
        };
    
         @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_messenger);
        }
    
        @Override
        protected void onStart() {
            super.onStart();
            Intent intent = new Intent(MessengerActivity.this, MessengerService.class);
            bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
        }
    
        @Override
        protected void onStop() {
            super.onStop();
            if(mBound){
                unbindService(mServiceConnection);
                mBound = false;
            }
        }
    }
    
    

    通过以上代码,可以看到Messenger的使用方法:

      1. 服务实现一个Handler,由其接收来自客户端的每个调用的回调。
      1. Handler用于创建Messenger对象(对Handler的引用)。
      1. Messenger创建一个IBinder,服务通过onBind()使其返回客户端。
      1. 客户端使用IBinder将Messenger(引用服务的Handler)实例化,然后使用后者将Message对象发送给服务。
      1. 服务在其Handler中(具体地讲,是在handleMessage()方法中)接收每个Message。

    这样,客户端并没有调用服务的“方法”。而客户端传递的“消息”(Message对象)是服务在其Handler中接收的。

    四 结语

    注意,绑定解绑服务最好在onstart和onStop内,在activity的生命周期里,onStart和onStop是activity在栈顶与否的出口和入口,我们的服务一般是绑定当前activity,顾在这两个位置比较合适。

    另外,如果需要服务端能够回应客户端,就和服务端一样,我们还需要创建一个Handler并创建一个新的Messenger,并把这个Messenger对象通过Message的replyTo参数传递给服务端,服务端通过这个replyTo参数就可以回应客户端。

    
        /**
         * 发送message并接收服务端的回复
         */
        public void sayHelloAndGetReply() {
            if (!mBound) {
                return;
            }
    
            //需要接收服务端的回复时,这样发送
            message = Message.obtain(null,1, 0, 0);
            Bundle bundle = new Bundle();
            bundle.putString("result", "服务端你好");
            message.setData(bundle);
            message.replyTo = localMessenger;
            try {
                mMessenger.send(message);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }
    
        class LocalHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                String result = msg.getData().getString("result");
                switch (msg.what) {
                    case 1:
                        //do something
                        ToastUtils.showLong("客户端收到服务端的回复:"+result);
                        break;
                    default:
                        super.handleMessage(msg);
                }
            }
        }
    
    class IncomingHandler extends Handler {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                switch (msg.what) {
                    case 0:
                        ToastUtils.showShort("服务端收到客户端的已连接消息");
                        break;
    
                    case 1:
                        String result = msg.getData().getString("result");
                        ToastUtils.showShort("服务端收到客户端发送的消息:" + result);
                       
                        //回复客户端
                        Message newMsg = Message.obtain();
                        Bundle bundle = new Bundle();
                        newMsg.what = 1;
                        bundle.putString("result", "客户端你好");
                        newMsg.setData(bundle);
                        try {
                            msg.replyTo.send(newMsg);
                        } catch (RemoteException e) {
                            e.printStackTrace();
                        }
                        break;
                }
            }
        }
    

    相关文章

      网友评论

        本文标题:024 Android多进程-Messenger与Message

        本文链接:https://www.haomeiwen.com/subject/bjwcoktx.html