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();
    }

}

其次服务端构造出对应的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);
    }
}

大家这里注意客户端获取了服务端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();
}

可以发现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);
    }
}

原来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);
}

其实就是和我们使用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);

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

将消息发送到服务端时,因为是跨进程,所以肯定需要用到序列化与反序列化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();
}

主要是看一下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
}

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

@Override
public android.os.IBinder asBinder() { 
    return this;
}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值