• binder通信之Messenger介绍


    Messenger是什么?
    Messenger是基于AIDL实现的轻量级IPC方案。
    这里立马就会有疑问为什么要它呢?明明有了aidl
    上节课大家学完了aidl进行binder通信是否感觉到使用起来其实还是有点复杂,毕竟通信什么的要写aidl,而且客户端和服务端都需要aidl文件,两个过程里面都需要,相对来说还是比较麻烦,对于项目过程中可能就是一些简单的跨进程数据传递,就是调用几个非常非常简单的方法,很多觉得都要写aidl成本比较大,那么有没有更加简单的方案来解决这个问题呢?
    那就是本节课要讲解的Messenger。
    那么接下来直接讲他的使用过程:

    1、服务端对Messenger的使用

    服务端需要实现一个Handler用于处理客户端发来的跨进程通信信息:

    package com.example.servicedemo;
    
    import android.app.Service;
    import android.content.Intent;
    import android.os.Handler;
    import android.os.IBinder;
    import android.os.Message;
    import android.os.Messenger;
    import android.util.Log;
    
    import androidx.annotation.NonNull;
    import androidx.annotation.Nullable;
    
    public class MessengerService extends Service {
        Handler messengerHandler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                switch (msg.what) {
                    case  1:
                        Log.i("test","MessengerService Messenger handleMessage msg = " + msg + " bundle  key value = " + msg.getData().getString("bundleKey"));
                        Messenger clientSend = msg.replyTo;
                        Message toClientMsg = Message.obtain();
                        toClientMsg.what = 2;
                       // toClientMsg.obj = "I am replay from Server";
                        try {
                            clientSend.send(toClientMsg);
                        }catch (Exception e) {
                            Log.i("test","MessengerService clientSend  error ",e);
                        }
                        break;
                }
                super.handleMessage(msg);
            }
        };
        Messenger messenger = new Messenger(messengerHandler);
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return messenger.getBinder();
        }
    
    }
    
    
    • 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

    其次服务端构造出对应的Messenger:
    服务端构造:
    Messenger messenger = new Messenger(messengerHandler);
    注意这里参数是messengerHandler即一个Handler
    最后,当服务端的onBinder回调时候要返回Messenger的IBinder对象给客户端

    2、客户端的使用

    客户端还是和以前一样通过bindService在ServiceConnection类的onServiceConnected获取到服务端的返回的IBinder,从而获取到服务端的Messenger代理类,调用send函数发送Message。所以Messenger能发送的信息只有Message能携带的信息。

    public class MainActivity extends AppCompatActivity {
        Handler messengerHandler = new Handler() {
            @Override
            public void handleMessage(@NonNull Message msg) {
                if (msg.what == 2) {
                    Log.i("test","messengerHandler handleMessage msg.what == 2 msg = " + msg);
                }
                super.handleMessage(msg);
            }
        };
        Messenger messengerClientSend = new Messenger(messengerHandler);
        Messenger messengerServer = null;
    
        FloatingActionButton fab1 = findViewById(R.id.fab1);
            fab1.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    Intent intent = new Intent(MainActivity.this,MessengerService.class);
                    Log.i("test","MessengerService  onClick ");
                    bindService(intent, new ServiceConnection() {
                        @Override
                        public void onServiceConnected(ComponentName name, IBinder service) {
                            try {
                                Log.i("test","MessengerService  onServiceDisconnected name = " +name);
                                messengerServer = new Messenger(service);
                                sendMessageToServer();
                            } catch (Exception e) {
                                e.printStackTrace();
                                Log.i("test","error " ,e);
                            }
                        }
    
                        @Override
                        public void onServiceDisconnected(ComponentName name) {
                            Log.i("test","client onServiceDisconnected name = " +name);
                        }
                    }, BIND_AUTO_CREATE);
                }
            });
    
    void sendMessageToServer() throws RemoteException {
            Message toServer = Message.obtain();
            toServer.replyTo = messengerClientSend;
            toServer.what = 1;
            //toServer.obj = "hello I send from client"; 注意不可以 传递非parcel的对象,这个只能给obj赋值为parcel类型对象否则报错
            Bundle bundle = new Bundle();
            bundle.putString("bundleKey","bundleValue Client");
            toServer.setData(bundle);
            messengerServer.send(toServer);
        }
    }
    
    • 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

    大家这里注意客户端获取了服务端IBinder对象后,用它来构造客户端的Messenger,
    messengerServer = new Messenger(service);
    有了服务端Messenger后,那么就可以通过它与服务端进行通信了,通信的内容载体是我们最为属性Message,对他就是和Handler搭配的Message,它就是具体消息体,即你需要发送什么消息,都是把内容转换成Message对象既可以,这里我们案例中传递一个Bundle的对象,这个Bundle对象可以利用键值对方式装载各种各样类型数据。和Intent传递数据Bundle是一样的。注意这里message对象还有一个属性是replyTo ,这个是Messenger类型的,字面意思就是说这个消息发送过去,如果对方需要回复,就可以通过消息中的replyTo 的Messenger对象来进行回复,这里是不是也和我们上节课讲的binder双向通信一样,所以说Messenger这种方式本身就相当于自带了双向通信。

    3 Messenger本质原理

    Messenger其实本质上也是使用aidl进行实现了,只是这个aidl是在Framework层面进行写好了,不需要你写,你也就没有在意,没有看到。这里对他的源码进行分析一下:

    private final IMessenger mTarget;
    public Messenger(Handler target) { 
        mTarget = target.getIMessenger();
    }
    public Messenger(IBinder target) { 
        mTarget = IMessenger.Stub.asInterface(target);
    }
    public void send(Message message) throws RemoteException { 
        mTarget.send(message);
    }
    public IBinder getBinder() { 
        return mTarget.asBinder();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    可以发现Messenger有两种构造函数,一个参数是Handler ,另一个是Ibinder。
    从Messenger的构造方法(IMessenger.Stub.asInterface())可以看出它底层应该是使用的AIDL搞的.getBinder()其实是将调用了mTarget.asBinder(),而mTarget是我们传进来的Handler里面拿出来的,跟进Handler.getIMessenger()看一下:

    final IMessenger getIMessenger() { 
        synchronized (mQueue) { 
            if (mMessenger != null) { 
                return mMessenger;
            }
            mMessenger = new MessengerImpl();
            return mMessenger;
        }
    }
    private final class MessengerImpl extends IMessenger.Stub { 
        public void send(Message msg) { 
            msg.sendingUid = Binder.getCallingUid();
            Handler.this.sendMessage(msg);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    原来IMessenger是Handler的内部类MessengerImpl,它只有一个send方法.结合上面Messenger的源码,我们发现调用Messenger的send方法其实就是调用这里的MessengerImpl的send方法,然后这个send里面将Message转发给Handler#sendMessage(),最后也就是去了Handler#handleMessage()里面接收到这个Message.

    MessengerImpl是继承自IMessenger.Stub,这一看就感觉是AIDL文件自动生成的嘛,easy.大胆猜测一下对应的aidl文件应该是IMessenger.aidl,我们去源码里面找IMessenger.aidl,果然在frameworks/base/core/java/android/os/IMessenger.aidl这个位置找到了它.内容如下:

    package android.os;
    import android.os.Message;
    /** @hide */
    oneway interface IMessenger { 
        void send(in Message msg);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    其实就是和我们使用AIDL一样将IXXX.Stub的父类IBinder通过onBind返回回去,客户端绑定的时候好拿到binder对象.接收客户端的消息时,是通过MessengerImpl转发给Handler来完成的,服务端这边定义的那个Handler就可以在handleMessage()中处理跨进程传递过来的Message,从而理解客户端想要调用什么服务,然后执行相应的逻辑.

    再看客户端这边,在onServiceConnected()时,将服务端返回的IBinder对象放进Messenger里.

    public void onServiceConnected(ComponentName name, IBinder service) {
                            try {
                               ...
                                messengerServer = new Messenger(service);
                                sendMessageToServer();
                            } catch (Exception e) {
                                e.printStackTrace();
                                Log.i("test","error " ,e);
                            }
    
    
    
    //Messenger.java
    public void send(Message message) throws RemoteException { 
        mTarget.send(message);
    }
    public Messenger(IBinder target) { 
                //这里asInterface 出来的其实就是 IMessenger.Stub.Proxy对象
        mTarget = IMessenger.Stub.asInterface(target);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    IBinder对象放进Messenger原来就是熟悉的操作IMessenger.Stub.asInterface(),简单.然后客户端这边给服务端发消息的时候通过构建出来的Messenger调用send方法发送,而Messenger内部send的实现其实就是调用IMessenger.Stub.Proxy(跨进程了)的send方法.调用之后,服务端那边在Handler的handleMessage里收到这条消息(Message),从而实现了跨进程通信.

    3.2 服务端->客户端通信

    客户端与服务端的通信与我们用AIDL的方式实现几乎一致,完全可以我们自己实现,Messenger只是帮我们封装好了而已.下面来看一下服务端与客户端的通信.

    服务端需要与客户端通信的话,需要客户端在send消息的时候将客户端Messenger存放在消息的replyTo中.

    
    Messenger messengerClientSend = new Messenger(messengerHandler);
    ...
    void sendMessageToServer() throws RemoteException {
            Message toServer = Message.obtain();
            toServer.replyTo = messengerClientSend;
            toServer.what = 1;
            //toServer.obj = "hello I send from client"; 注意不可以 传递非parcel的对象,这个只能给obj赋值为parcel类型对象否则报错
            Bundle bundle = new Bundle();
            bundle.putString("bundleKey","bundleValue Client");
            toServer.setData(bundle);
            messengerServer.send(toServer);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    将消息发送到服务端时,因为是跨进程,所以肯定需要用到序列化与反序列化Message.看下Message的反序列化代码:

    private void readFromParcel(Parcel source) { 
        what = source.readInt();
        arg1 = source.readInt();
        arg2 = source.readInt();
        if (source.readInt() != 0) { 
            obj = source.readParcelable(getClass().getClassLoader());
        }
        when = source.readLong();
        data = source.readBundle();
        replyTo = Messenger.readMessengerOrNullFromParcel(source);
        sendingUid = source.readInt();
        workSourceUid = source.readInt();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    主要是看一下replyTo是怎么反序列化的,它调用了Messenger的readMessengerOrNullFromParcel方法:

    public static void writeMessengerOrNullToParcel(Messenger messenger,
            Parcel out) { 
        out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
                : null);//这里用asBinder()方法转成IBinder
    }
    public static Messenger readMessengerOrNullFromParcel(Parcel in) { 
        IBinder b = in.readStrongBinder();
        return b != null ? new Messenger(b) : null;  //这里重新用IBinder构造了Messenger
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    writeMessengerOrNullToParcel中将客户端的messenger.mTarget.asBinder()进行了写入,然后在readMessengerOrNullFromParcel时进行了恢复,而messenger.mTarget就是上面分析的MessengerImpl,asBinder()是其父类IMessenger.Stub里面的一个方法:

    @Override
    public android.os.IBinder asBinder() { 
        return this;
    }
    
    • 1
    • 2
    • 3
    • 4

    就是将自身返回出去.也就是说,服务端反序列化出来的replyTo对应Messenger中的IBinder其实就是客户端的MessengerImpl对象.于是服务端拿到这个Messenger就可以发送消息,通过这个IBinder对象跨进程通信,客户端就接收到消息了.

  • 相关阅读:
    ACDSee Photo Studio Ultimate 2024特别版(图片编辑器)
    从数据仓库到数据湖、湖仓一体:概念溯源分析底层逻辑
    三年后端开发: 拿下阿里 / 腾讯 / 美团等四个大厂的 Offer 后,总结如下
    Flutter快学快用18 项目实战:实践 Flutter 交友功能
    linux下的PPPOE设置
    堆的实现+堆的应用(堆排序和Topk)
    软件测试/测试开发丨人工智能产品质量保障:挑战与创新
    .NET性能优化-是时候换个序列化协议了
    <Linux> shell运行原理及Linux权限的理解
    spring整合struts2 以及 会遇到的问题
  • 原文地址:https://blog.csdn.net/qq_34888036/article/details/133759083