Messenger源码解析

来源:互联网 发布:dwg是什么软件 编辑:程序博客网 时间:2024/05/01 08:33

之前根据官方文档和网上一些大神的博客,已经可以正常的使用Messenger进行跨进程通讯了,其实流程很简单,大体如下:
此处输入图片的描述
在使用层面已经熟悉了,最好还是看一下源码,系统是怎么封装的,原理是什么,虽然网上已经有很多类似的,但是自己写,自己看,学到就是自己的。我们就根据流程图的单向通讯分析(Client向Service发送一条消息)。

  1. 构造Messenger
    private final IMessenger mTarget;    public Messenger(Handler target) {        mTarget = target.getIMessenger();    }

看到,我们传Handler过来,只是为了获取IMessenger对象。追踪到Handler源码看一下

    final IMessenger getIMessenger() {        synchronized (mQueue) {            if (mMessenger != null) {                return mMessenger;            }            mMessenger = new MessengerImpl();            return mMessenger;        }    }

这里我们看到mMessenger的实现是MessengerImpl这个类,他是Hanler的内部类。

    private final class MessengerImpl extends IMessenger.Stub {        public void send(Message msg) {            msg.sendingUid = Binder.getCallingUid();            Handler.this.sendMessage(msg);        }    }

总共就这几行,很简单,做的事情也很简单,只有一个send方法,把发起调用的客户端进程的 Linux Uid存储在我们传入的 Message 对象中,然后handler把Message发送出去。

  1. 构造好Messenger,建立连接,接下来就是发送消息了吧,
    public void send(Message message) throws RemoteException {        mTarget.send(message);    }

这里就是发送的源码,他的实现就是上面我们说的MessengerImpl中的send();
Messenger发送消息是经由Handler实现的,所以Messenger的消息是以MessageQueue去管理的,也就是一次只能处理一个消息,不能支持并发任务

  1. 再来看一下IMessenger
    发现底层的实现还是用的AIDL,
//Messenger.aidlpackage android.os;parcelable Messenger;//IMessenger.aidlpackage android.os;import android.os.Message;/** @hide 注意这里的oneway关键字*/oneway interface IMessenger {    void send(in Message msg);}

这也就不难理解Messenger中出现的两个方法了

//客户端调用    public Messenger(IBinder target) {        mTarget = IMessenger.Stub.asInterface(target);    }    //服务端调用        public IBinder getBinder() {        return mTarget.asBinder();    }

既然底层是AIDL实现,那么那就应该符合AIDL的调用流程
此处输入图片的描述

看过aidl的源码实现,我们知道不能在客户端进程的 UI 线程中发起远程方法调用,不然如果远程方法执行了耗时操作,客户端的UI线程将会被阻塞,从而造成 ANR 的问题存在。这个我亲测过,不信你就自己写一个试试。

但是在Messenger貌似不存在这种情况,无论在服务端执行多么耗时的任务,都不会卡顿UI线程。
这是不是有点奇怪,因为其中的交互只有一个Handler,而且还是绑定UI线程的Handler,按说在handleMessage()中执行耗时任务,肯定会阻塞UI线程。然而并没有,肯定是哪里我们忽略了。还记得前面那个oneway关键字吗。google文档中的介绍:

The oneway keyword modifies the behavior of remote calls. When used, a remote call does not block; it simply sends the transaction data and immediately returns. The implementation of the interface eventually receives this as a regular call from the Binder thread pool as a normal remote call. If oneway is used with a local call, there is no impact and the call is still synchronous.

这就是关键,允许客户端能够非阻塞的调用远程方法,之前定义的IMessenger.aidl是不是就是用了oneway关键字,再看一下生成.java有和不同:

//IMessage.Proxy.sendpublic 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);// } else {_data.writeInt(0);}// 这里如果不加oneway关键字,proxy中的send方法,mRemote.transact(,,,0)最后这个参数是0,加了之后就变为了android.os.IBinder.FLAG_ONEWAYmRemote.transact(Stub.TRANSACTION_send, _data, null,android.os.IBinder.FLAG_ONEWAY);} finally {_data.recycle();}}

查看 API 文档即可以看到 FLAG_ONEWAY 的作用就是让客户端能够非阻塞的调用远程方法,至此真相大白,如果我们自定义的 aidl 也想实现非阻塞的调用,只需声明 oneway 关键字即可。

4.总结,Messenger的工作原理这下就清楚了,传递消息依赖的是Handler,但是此处是不阻塞UI线程的,这也是Messenger的神奇之处。

0 0
原创粉丝点击