Messenger是如何封装AIDL接口的?

Messenger 可以用于进程间通讯,其底层就是使用 AIDL 实现。Messenger 封装了一个IMessenger 类型的属性 mTarget ,它是 Messenger 实现进程间通讯的核心类。

1、创建 Messenegr

上面的描述可能有点模糊,没关系先来了解一下如何创建 Messenegr :

  • 如果想让 Messenger 作为服务端接收客户端的消息,那么创建 Messenger 对象的方式就是调用带有 Handler 的构造方法。看了下面的源码我们大概知道 mTarget 就是 IMessenger 真正实现类,而这里得 mTarget 就是 MessengerImpl 对象。

这里涉及到一个 Handler ,它得作用是什么呢?因为客户端需要给服务端发送消息,而学过 AIDL 的同学都知道,客户端其实操作的是服务端远程接口的代理对象,真正实现功能就是 MessengerImpl 这个对象,代理对象实际通过 Binder 调用 MessengerImpl 中真正的方法去发送 message,而 Handler 就是用于调度 message 信息。

private final IMessenger mTarget;

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

final IMessenger getIMessenger() {
    synchronized (mQueue) {
        if (mMessenger != null) {
            return mMessenger;
        }
        mMessenger = new MessengerImpl();
        return mMessenger;
    }
}
  • 如果想让 Messenger 作为客户端给服务端发送消息,那么创建 Messenger 对象的方式就是调用带有 IBinder 的构造方法,其内部将 IBinder 转化为 IMessenger 的代理对象。
public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}

我们在上面讲了,客户端给服务端发送消息实际上操作的是远程服务接口的代理对象 mTarget ,它内部会调用远程接口的真正实现类 MessengerImpl 完成消息的发送。

2、服务端与客户端通讯示例代码

2.1、服务端实现

  • 根据上面的知识点描述,作为服务端的 Messenger 是通过带有 Handler 的构造方法去创建的,这时相当把远程服务的真正实现类 MessengerImple 创建好了,而 mTarget 就是指向该 MessengerImpl 对象。
public Messenger(Handler target) {
    //真正实现类
    mTarget = target.getIMessenger();
}

final IMessenger getIMessenger() {
    synchronized (mQueue) {
        if (mMessenger != null) {
            return mMessenger;
        }
        //实际返回的是 MessengerImpl 对象
        mMessenger = new MessengerImpl();
        return mMessenger;
    }
}
  • messenger.getBinder()将该实现类转化为 Binder 传递给客户端。
public IBinder getBinder() {
    //将第一步创建的 mTarget 实体对象转化为 Binder
    return mTarget.asBinder();
}
  • 实现Handler接收客户端发送过来的 Message 消息。
    • 获取客户端传递过来的消息;
    • msg.replyTo获取客户端带过来的 Messenger 对象 client;
    • 构造一个你要发送的消息;
    • 通过 client 去发送这个消息。
private static class MessengerHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 1:
                //接受客户端传递过来的消息
                Bundle bundle = msg.getData();
                System.out.println(bundle.getString("msg"));
                //回应客户端
                //拿到这个 msg 对应的 Messenger
                Messenger client = msg.replyTo;
                //构造一个你要发送的消息
                Message message=Message.obtain();
                message.what=2;
                Bundle data = new Bundle();
                data.putString("msg","服务端收到了客户端的消息拉");
                message.setData(data);
                //通过 Messenger 发送消息给客户端
                try {
                    client.send(message);
                } catch (RemoteException e) {
                    e.printStackTrace();
                }
                break;
        }
    }
}
  • 完整代码实现
/**
 * 服务端收到客户端的消息之后,回应客户端
 *
 */
public class MessengerService extends Service {
    private static class MessengerHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            switch (msg.what) {
                case 1:
                    //接受客户端传递过来的消息
                    Bundle bundle = msg.getData();
                    System.out.println(bundle.getString("msg"));

                    //回应客户端
                    //拿到这个 msg 对应的 Messenger
                    Messenger client = msg.replyTo;

                    //构造一个你要发送的消息
                    Message message=Message.obtain();
                    message.what=2;

                    Bundle data = new Bundle();
                    data.putString("msg","服务端收到了客户端的消息拉");

                    message.setData(data);
                    //通过 Messenger 发送消息给客户端
                    try {
                        client.send(message);
                    } catch (RemoteException e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }
    }

    private final Messenger messenger = new Messenger(new MessengerHandler());

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

2.2、客户端实现

  • 当前的 Messenger 是作为客户端的,应该通过带有 IBinder 的构造方法去创建 Messenger 对象。在内部会将该 IBinder 转化为IMessenger 的代理对象mServiceMessenger。


//根据服务端传来的 IBinder 对象创建一个 Messenger
mServiceMessenger = new Messenger(service);
   

//通过 IBinder 创建 Messenger
public Messenger(IBinder target) {
    mTarget = IMessenger.Stub.asInterface(target);
}
  • 创建一个用于服务端给客户端回应消息的 Messenger 载体
private final Messenger mGetReplyMessenger = new Messenger(new GetMessageHandler());
  • 用于接收处理服务端回应客户端消息的 Handler
private static class GetMessageHandler extends Handler {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        switch (msg.what) {
            case 2:
                Bundle data = msg.getData();
                String result = data.getString("msg");
                System.out.println("receive from server:"+result);
                break;
        }
    }
}
  • 构建一个 message 对象,并通过该代理对象进行发送,这个过程就是进行 IPC 进程间通讯。想一下这里为什么要使用 message 呢?因为代理对象的 send 方法发送的就是 message 对象,并且 message 实现了 Parcelable 接口,因此可以作为数据的载体进行进程间通讯。而且刚才说过,最终真正发送消息的是服务端远程接口的真正实现类 MessengerImpl 去发送的,并且由 handler 去调度 message,这里的 handler 刚好可以处理 message 消息。这里给 message.replyTo 赋值了一个在客户端创建的 Messenger 对象,目的是为了在服务端能通过该 Messenger 给客户端回应消息。
//当服务建立连接之后就发送数据给服务端
Message msg = Message.obtain();
msg.what = 1;
//跨进程通讯不能使用 message.obj 这个属性,应该使用 bundle 否则会出现Can't marshal non-Parcelable ob
//msg.obj = "say hello to server";
Bundle bundle = new Bundle();
bundle.putString("msg", "say hello to server");
msg.setData(bundle);
//将 Messenger 传递给服务端
msg.replyTo = mGetReplyMessenger;
  • 完整代码
public class MainActivity extends AppCompatActivity {
    private Messenger mServiceMessenger;

    private static class GetMessageHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);

            switch (msg.what) {
                case 2:
                    Bundle data = msg.getData();
                    String result = data.getString("msg");
                    System.out.println("receive from server:"+result);

                    break;
            }
        }
    }

    private final Messenger mGetReplyMessenger = new Messenger(new GetMessageHandler());

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Intent intent = new Intent(this, MessengerService.class);

        //绑定服务
        bindService(intent, conn, Service.BIND_AUTO_CREATE);
    }

    private ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //根据服务端传来的 IBinder 对象创建一个 Messenger
            mServiceMessenger = new Messenger(service);

            //当服务建立连接之后就发送数据给服务端
            Message msg = Message.obtain();
            msg.what = 1;
            //跨进程通讯不能使用 message.obj 这个属性,应该使用 bundle 否则会出现Can't marshal non-Parcelable objects across processes.
            //msg.obj = "say hello to server";
            Bundle bundle = new Bundle();
            bundle.putString("msg", "say hello to server");
            msg.setData(bundle);
            //将 Messenger 传递给服务端
            msg.replyTo = mGetReplyMessenger;
            //给远程服务发送消息
            try {
                mServiceMessenger.send(msg);
            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (conn != null) {
            unbindService(conn);
        }
    }
}

3、使用 Message.replyTo 属性将 Messenger 传递给服务端

这里涉及到给客户端回应消息的情况,其过程大概是这样的,首先客户端给服务端发送消息 message ,并且在该 message.replyTo 属性添加在客户端创建的 Messenger 对象(实际上这时的 Messenger 就是 IMessener 的真正实现者),它内部维护了一个 handler 对象,当服务端获取到这个客户端通过 message 传递过来的 Messenger 对象(实际上就是 IMessenger 的代理对象)之后,构造一个 message 对象(服务端回应给客户端的消息)并通过 messenger 发送给客户端。这里要注意:Messenger 是实现了 Parcelable 接口,因此它可以在进程间进行传输。

public final class Messenger implements Parcelable

我们都知道进程间通讯就是对传输的数据进行序列化和反序列化的过程,而 Messenger 是可以进行进程间传输的,我们在最开始的时候提过, Messenger 实际上是对 IMessenger 的封装,所以将 Messenger 传递给服务端,我们应该更加关注其封装的 IMessnger 是怎么被被转化为 IBinder 对象传递过去。因此我们想要了解 Messenger 是怎么被传输的,那么直接看 replyTo 携带的 Messenger 的内部是怎么实现 Parcelable 的序列化和反序列化就好了

  • 序列化
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);
    //将replyTo这个 Messenger 进行序列化
    Messenger.writeMessengerOrNullToParcel(replyTo, dest);
    dest.writeInt(sendingUid);
}
  • writeMessengerOrNullToParcel

下面代码就是将 Messenger 内部的 mTarget 转化为 IBinder 进行远程传输。

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

Messenger 序列化过程就是将 IMessenger 实体转化为 IBinder ,而反序列化就是将获取到的 IBinder 转化未 IMessenger 对象的过程(在 Messenger 构造实现)。

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

4、知识点

  • 了解使用 Messenger 的基本步骤。
  • Messenger 充当客户端和服务端时内部的 mTarget 的实现是不一样的。
  • 了解 Messenger 是怎么发送消息。
  • Messenger 也是可以进行传输的,但是需要了解内部是怎么处理 message.replyTo 属性。
  • Messenger 是串行执行的,服务端一次处理一个客户端请求。
  • Messenger 只适合做到传递消息,而不能做到调用服务端的方法。
  • Messenger 传输数据的载体是 Message 对象,而需要注意 message 的 obj 属性只能用于系统定义的 Parcelable 实现类。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,905评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,140评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,791评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,483评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,476评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,516评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,905评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,560评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,778评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,557评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,635评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,338评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,925评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,898评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,142评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,818评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,347评论 2 342

推荐阅读更多精彩内容