Messenger 跨进程通讯及源码解析

上一章讲了 aidl,这一章讲讲 Messenger, Messenger 是对 aidl 的封装,使用起来比较简单。我们先来看看这个类的源码

public final class Messenger implements Parcelable {
    private final IMessenger mTarget;

    public Messenger(Handler target) {
        mTarget = target.getIMessenger();
    }

    public void send(Message message) throws RemoteException {
        mTarget.send(message);
    }

    public IBinder getBinder() {
        return mTarget.asBinder();
    }

    public boolean equals(Object otherObj) {
        if (otherObj == null) {
            return false;
        }
        try {
            return mTarget.asBinder().equals(((Messenger)otherObj)
                    .mTarget.asBinder());
        } catch (ClassCastException e) {
        }
        return false;
    }

    public int hashCode() {
        return mTarget.asBinder().hashCode();
    }

    public int describeContents() {
        return 0;
    }

    public void writeToParcel(Parcel out, int flags) {
        out.writeStrongBinder(mTarget.asBinder());
    }

    public static final Parcelable.Creator<Messenger> CREATOR
            = new Parcelable.Creator<Messenger>() {
        public Messenger createFromParcel(Parcel in) {
            IBinder target = in.readStrongBinder();
            return target != null ? new Messenger(target) : null;
        }

        public Messenger[] newArray(int size) {
            return new Messenger[size];
        }
    };

    public static void writeMessengerOrNullToParcel(Messenger messenger,
                                                    Parcel out) {
        out.writeStrongBinder(messenger != null ? messenger.mTarget.asBinder()
                : null);
    }

    public static Messenger readMessengerOrNullFromParcel(Parcel in) {
        IBinder b = in.readStrongBinder();
        return b != null ? new Messenger(b) : null;
    }

    public Messenger(IBinder target) {
        mTarget = IMessenger.Stub.asInterface(target);
    }
}

Messenger 实现了 Parcelable 接口,可以被序列化,describeContents()、writeToParcel() 和 CREATOR 是实现的方法;Messenger 的构造方法有两个,构造参数分别为Handler 和 IBinder,相同的一点都是从 Handler 或 IBinder 中获取 IMessenger mTarget,从这里可以看出来,IMessenger 也实现了 IInterface 接口;getBinder() 方法返回的是 mTarget.asBinder();  equals(Object otherObj) 方法比较的是Messenger中包含的 IBinder; send(Message message) 方法最终调用的是 IMessenger 的 send(message) 方法; writeMessengerOrNullToParcel() 和 readMessengerOrNullFromParcel() 方法则是跨进程通讯时,序列化和反序列化时转换数据时用的。

我们先说一下用法,它是对aidl的封装,当然也是通过 bindService ,在 Service 中 接收消息和回传消息,我们写出跨进程的代码, 在同一个apk中模拟两个不同的进程
 客户端  Activity
 
     private void binderServe() {
        bindService(new Intent(this, MessengerService.class),
                mConnection, Context.BIND_AUTO_CREATE);
    }

    boolean mIsMessenger = false;
    private ServiceConnection mConnection = new ServiceConnection(){
        public void onServiceConnected(ComponentName className, IBinder service) {
            mIsMessenger = true;
            // 向远程服务发消息
            Messenger messenger = new Messenger(service);
            Message msg = Message.obtain(null, 1);
            try {
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mIsMessenger = false;
        }
    };

    @Override
    protected void onStop() {
        super.onStop();
        // 解绑远程服务
        if (mIsMessenger) {
            unbindService(mConnection);
        }
    }

服务端  Service

public class MessengerService extends Service {
    public MessengerService() {
    }

    final Messenger mServiceMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // 接收客户端消息
            switch (msg.what) {
                case 1:
                    Log.e("messenger", "service ! ");
                    break;
            }
        }
    });

    @Override
    public IBinder onBind(Intent intent) {
        return mServiceMessenger.getBinder();
    }
}

 <service android:name=".service.MessengerService" android:process=":remote2"/>

我们点击按钮,执行 binderServe() 方法,就会看到打印log日志  messenger: service !  说明activity 发送 Message,传递给了不同进程的 Service ,Service 接收后,通过 Handler 来接收到信息,然后打印了出来。如果我们 Service 也想传递信息给 Activity ,该怎么办? Messenger 同样封装好了,我们对 Activity 稍作修改
    private Messenger mMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg){
            // 接收远程服务消息
            switch (msg.what){
                case 1:
                    Log.e("messenger", "activity ! ");
                    break;
            }
        }
    });

    private ServiceConnection mConnection = new ServiceConnection(){
        public void onServiceConnected(ComponentName className, IBinder service) {
            mIsMessenger = true;
            // 向远程服务发消息
            Messenger messenger = new Messenger(service);
            Message msg = Message.obtain(null, 1);
            
            // 注意重点,在这一步,把 mMessenger 传递给 Message,当做它的一个属性
            msg.replyTo = mMessenger;
            
            
            try {
                messenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        public void onServiceDisconnected(ComponentName className) {
            mIsMessenger = false;
        }
    };

再看看 Service 端

    final Messenger mServiceMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg) {
            // 接收客户端消息
            switch (msg.what) {
                case 1:
                    Log.e("messenger", "service ! ");
                    try {
                        // 向客户端发消息, 这里 msg.replyTo 就是 activity 中传入的对象
                        Messenger messenger = msg.replyTo;
                        messenger.send(Message.obtain(null, 1));
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    });

重复开始的操作,我们点击按钮,看一下打印的日志
 E/messenger: service ! 
 E/messenger: activity ! 
说明双向通讯成功。

我们知道,通过 aidl 跨进程通讯,客户端是同步的,一直处于UI线程,如果服务端有耗时操作,客户端很可能就ANR异常了,那么 Messenger 有这个问题吗?试一下
在Service中打印log日志的后面,添加一个线程睡眠
        try {
            Thread.sleep(6000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

重复一开始的操作,打印日志

05-30 16:17:34.624 32674-32674/? E/messenger: service !
05-30 16:17:40.625 32511-32511/E/messenger: activity !

客户端没有出现ANR异常,这是怎么回事呢?还有,messenger.send(msg) 又是怎么发送信息呢? 我们还是先来看看 IMessenger 的源码

public interface IMessenger extends android.os.IInterface {

    public static abstract class Stub extends android.os.Binder implements android.os.IMessenger {
        private static final java.lang.String DESCRIPTOR = "android.os.IMessenger";

        public Stub() {
            this.attachInterface(this, DESCRIPTOR);
        }

        public static android.os.IMessenger asInterface(android.os.IBinder obj) {
            if ((obj == null)) {
                return null;
            }
            android.os.IInterface iin = (android.os.IInterface) obj
                    .queryLocalInterface(DESCRIPTOR);
            if (((iin != null) && (iin instanceof android.os.IMessenger))) {
                return ((android.os.IMessenger) iin);
            }
            return new android.os.IMessenger.Stub.Proxy(obj);
        }

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

        @Override
        public boolean onTransact(int code, android.os.Parcel data,
                                  android.os.Parcel reply, int flags)
                throws android.os.RemoteException {
            switch (code) {
                case INTERFACE_TRANSACTION: {
                    reply.writeString(DESCRIPTOR);
                    return true;
                }
                case TRANSACTION_send: { // 接收的code和发送的code都是TRANSACTION_send
                    data.enforceInterface(DESCRIPTOR);
                    android.os.Message _arg0;
                    if ((0 != data.readInt())) { // data.readInt为1(当msg不为空时)
                        _arg0 = android.os.Message.CREATOR.createFromParcel(data);
                    } else {
                        _arg0 = null;
                    }
                    this.send(_arg0);
                    return true;
                }
            }
            return super.onTransact(code, data, reply, flags);
        }

        private static class Proxy implements android.os.IMessenger {
            private android.os.IBinder mRemote;

            Proxy(android.os.IBinder remote) {
                mRemote = remote;
            }

            public android.os.IBinder asBinder() {
                return mRemote;
            }

            public java.lang.String getInterfaceDescriptor() {
                return DESCRIPTOR;
            }

            public void send(android.os.Message msg)
                    throws android.os.RemoteException {
                android.os.Parcel _data = android.os.Parcel.obtain();
                try {
                    _data.writeInterfaceToken(DESCRIPTOR);
                    if ((msg != null)) {
                        _data.writeInt(1);
                        msg.writeToParcel(_data, 0);// 将msg所有字段(what,obj)写到_data中去
                    } else {
                        _data.writeInt(0);
                    }
                    mRemote.transact(Stub.TRANSACTION_send, _data, null, // // 接收的code和发送的code都是TRANSACTION_send
                            android.os.IBinder.FLAG_ONEWAY);
                } finally {
                    _data.recycle();
                }
            }
        }

        static final int TRANSACTION_send = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
    }

    public void send(android.os.Message msg) throws android.os.RemoteException;

}

IMessenger 果然是继承了IInterface,内部抽象类 Stub 是关键,妥妥的一个 aidl ,咱们重点看 onTransact() 方法中 case TRANSACTION_send 的代码

        case TRANSACTION_send: { // 接收的code和发送的code都是TRANSACTION_send
            data.enforceInterface(DESCRIPTOR);
            android.os.Message _arg0;
            if ((0 != data.readInt())) { // data.readInt为1(当msg不为空时)
                _arg0 = android.os.Message.CREATOR.createFromParcel(data);
            } else {
                _arg0 = null;
            }
            this.send(_arg0);
            return true;
        }
data.readInt() != 0 说明有内容,对应的是 Proxy 中 send() 方法
        if ((msg != null)) {
            _data.writeInt(1);
            msg.writeToParcel(_data, 0);// 将msg所有字段(what,obj)写到_data中去
        } else {
            _data.writeInt(0);
        }
这一段代码。重点来了 android.os.Message.CREATOR.createFromParcel(data) 这个明显就是反序列化的代码,Message 也实现了Parcelable接口, 看看 Message 中相关部分

    public static final Parcelable.Creator<Message> CREATOR
            = new Parcelable.Creator<Message>() {
        public Message createFromParcel(Parcel source) {
            Message msg = Message.obtain();
            msg.readFromParcel(source);
            return msg;
        }
        
        public Message[] newArray(int size) {
            return new Message[size];
        }
    };

    public void writeToParcel(Parcel dest, int flags) {
        if (callback != null) {
            throw new RuntimeException(
                "Can't marshal callbacks across processes.");
        }
        dest.writeInt(what);
        dest.writeInt(arg1);
        dest.writeInt(arg2);
        if (obj != null) {
            try {
                Parcelable p = (Parcelable)obj;
                dest.writeInt(1);
                dest.writeParcelable(p, flags);
            } catch (ClassCastException e) {
                throw new RuntimeException(
                    "Can't marshal non-Parcelable objects across processes.");
            }
        } else {
            dest.writeInt(0);
        }
        dest.writeLong(when);
        dest.writeBundle(data);
        Messenger.writeMessengerOrNullToParcel(replyTo, dest);
        dest.writeInt(sendingUid);
    }

    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();
    }
这三段代码我们可以明白 Message 如何序列化和反序列化的。

Message _arg0 = android.os.Message.CREATOR.createFromParcel(data); 获取到 Message 对象后,然后 this.send(_arg0); 发送信息,我们知道,send(android.os.Message msg) 是个抽象方法,具体的要找到它的实现类。Service端中 MessengerService 发送信息的方法,Messenger messenger = msg.replyTo; messenger.send(Message.obtain(null, 1));这里的 Messenger 是 msg.replyTo,那么 msg.replyTo 是怎么来的呢?原来是 Activity 中传递进来的, msg.replyTo = mMessenger; 这个mMessenger 是
    private Messenger mMessenger = new Messenger(new Handler(){
        @Override
        public void handleMessage(Message msg){
            // 接收远程服务消息
            switch (msg.what){
                case 1:
                    Log.e("messenger", "activity ! ");
                    break;
            }
        }
    });
这里使用的构造方法是 new Messenger(new Handler()),我们知道 public Messenger(Handler target) {mTarget = target.getIMessenger();},看看Handler中的代码
    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);
        }
    }
原来,最终调用的是 Handler.this.sendMessage(msg); 跨线程耗时操作,通过这里最后一步,会进入到UI线程中。为什么没有造成Activity的ANR异常,却还是不清楚,后来查资料,才知道 IMessenger.aidl 接口前面有 oneway 关键字修饰 oneway interface IMessenger{void send(in Message msg)}。没有 oneway 时,mRemote.transact()的最后一个参数是 0;声明 oneway 修饰时,mRemote.transact() 的最后一个参数是 android.os.IBinder.FLAG_ONEWAY, FLAG_ONEWAY 的作用是让客户端可以非阻塞的调用远程方法。还有一点,Messenger 不适合高并发的场景。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值