美文网首页
Android 消息机制原理及源码学习

Android 消息机制原理及源码学习

作者: JokerHerry | 来源:发表于2017-11-30 20:37 被阅读0次

    Handler是我们常用的东西,为什么呢?因为他可以将我们所需要的反馈事件切换到handler所在的线程中运行,从而实现在子线程中实现耗时间的操作,然后将返回的结果通知到主线程,然后实现更新UI的过程。今天我们就从实现原理以及源码的角度分析一下,Android的消息机制,看看handler是怎样实现线程切换的。

    消息机制的实现

    基本实现方法

    我们首先来实现一个最基本的通过handler的消息传递。

    public void showDialog(String str) {
        Message message = new Message();
        message.what = START_DIALOG;
        Bundle bundle = new Bundle();
        bundle.putString("str",str);
        message.setData(bundle);
        handler.sendMessage(message);
    }
    
    Handler handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case STOP_DIALOG:
                        DialogUtil.getIntance().closeDialog();
                        break;
                    case START_DIALOG:
                        DialogUtil.getIntance().waitDialog(BaseActivity.this).show();
                        break;
                    default:
                        break;
                }
            }
        };
    

    在实例中,我们通过实现了一个handler,然后通过调用showDialog,达到显示dialog的目的。

    但是在完成这一过程中,不仅仅只是一个Handler就完成了所有的操作,而且handler只是一个用于帮助我们快速时间的工具。

    消息机制组成元素

    消息的传递我们一共可以分为3个部分,Handler,MessageQueue,Looper。
    Messag只是起我们作为传递过程中,传递参数的作用。

    Message:线程之间的消息传递的信息载体,存放少量信息。可以传递的信息有int(what arg1 arg2) Object (obj).

    Handler:事件的发出者和处理者,消息机制的辅助处理类。主要通过Handler.sendMessage()发出信息,和Handler.handleMessage()处理信息。

    MessageQueue:一个实现了先进先出效果的单链表,并不是我们所谓的队列数据结构。主要用于存放所有通过Handler.sendemessage发送的消息。即向消息池投递消息(MessageQueue.enqueueMessage)和取走消息池的消息(MessageQueue.next)。会一直存在消息队列中,等待被处理。每个线程只有一个MessageQueue对象。

    Looper:在(Looper.loop)中不断循环执行,从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。即将事件交给所对应的Handler处理。

    然后我们就可以理解到,消息传递就是将消息先放到每个线程中的MessageQueue中,然后通过Looper将MessageQueue中的Message发送到所对应的Handler中去。让Handler去处理这些事件。

    从而达到将一个线程中的事件传递到Handler所在的线程中去。

    消息机制模型

    然后我们再用图解的方式,看一下之间的实现关系。


    消息机制流程

    我们按照流程图,一步一步分析消息的传递。
    首先在新线程中,我们实现了执行了耗时间操作,现在需要通知主线程更改UI。

    • 通过Handler的引用,发送一个Message。handler.sendMessage(message);
    • 通过Handler调度,将使用Handler.enqueueMessage将消息传递到MessageQueue的队列中。
    • 存放在MessageQueue中的消息,将会因为Looper.loop()的开启,一直调用MessageQueue.next,一直从队列中取出消息。
    • 在Looper中被取出的Message将会通过msg.target.dispatchMessage(msg);将会分发一个事件给我们所引用的Handler。
    • Handler的到事件,进行处理。更改UI。

    注意:
    1.每个线程中只有一个Looper实例。而MessageQueue是在Looper中实例的,所以MessageQueue也只有一个。
    2.实际上每个线程中的Looper都是要我们自己实现的,只不过因为在Activity中,在ActivityThread中就已经帮我们Looper.prepareMainLooper();Looper.loop();了,所以我们才能直接使用Handler,但在新开的线程中,我们就要自己通过这两句代码开始新线程中的Looper。

    以上是对消息机制的实现以及消息传递的基本流程。下面我们将通过源码的方式来理解内部的实现原理。

    源码以及理解

    我们还是按照上面的流程图,一个个分析。


    消息机制流程
    发送消息
    • 创建handler
      开发文档中的创建Handler方法.png
        //申明默认的Handler
        public Handler() {
            this(null, false);
        }
        public Handler(Callback callback, boolean async) {
            if (FIND_POTENTIAL_LEAKS) {
            ......
            //必须先执行Looper.prepare(),才能获取Looper对象,否则为null.
            mLooper = Looper.myLooper();
            if (mLooper == null) {
                throw new RuntimeException(
                    "Can't create handler inside thread that has not called Looper.prepare()");
            }
           
            mQueue = mLooper.mQueue;   //从这我们看出MessageQueue是在Looper中实例的
            mCallback = callback;      //回调方法
            mAsynchronous = async;     //设置消息是否为异步处理方式
        }
    
    • 发送消息
      发送消息我们有种方式发送消息,一种是通过send方法发送message,一种是使用post方法,传递一个Runnable,当该条消息被发送给对应的handler的时候,handler直接执行内部代码。典型例子就是Android中的runOnUiThread(),直接将内部代码交由UI线程的中的handler处理。
      send
      send.png
      post
      post.png

    但不管是send还是post发送信息,最终都会调用sendMessageAtTime这个函数。

        public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
            //取得消息队列
            MessageQueue queue = mQueue;
            if (queue == null) {
                RuntimeException e = new RuntimeException(
                        this + " sendMessageAtTime() called with no mQueue");
                Log.w("Looper", e.getMessage(), e);
                return false;
            }
            return enqueueMessage(queue, msg, uptimeMillis);
        }
    

    并且就连子线程中调用Activity中的runOnUiThread()中更新UI,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。

    public final void runOnUiThread(Runnable action) {
            if (Thread.currentThread() != mUiThread) {
                mHandler.post(action);
            } else {
                action.run();
            }
        }
    

    在看看sendMessageAtTime最后调用的enqueueMessage

        private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {    
            //target就是发送message的那个handler的引用
            msg.target = this;
            if (mAsynchronous) {
                //是否异步执行
                msg.setAsynchronous(true);
            }
            //将消息放置在消息队列中
            return queue.enqueueMessage(msg, uptimeMillis);
        }
    

    可以看出,Handler通过对Looper中的MessageQueue的引用,将得到的信息放到消息队列中。

    传递消息

    MessageQueue

        boolean enqueueMessage(Message msg, long when) {
            //target就是发送message的那个handler的引用
            if (msg.target == null) {
                throw new IllegalArgumentException("Message must have a target.");
            }
            if (msg.isInUse()) {
                throw new IllegalStateException(msg + " This message is already in use.");
            }
    
            synchronized (this) {
                if (mQuitting) {    //正在退出时,回收msg,加入到消息池
                    IllegalStateException e = new IllegalStateException(
                            msg.target + " sending message to a Handler on a dead thread");
                    Log.w(TAG, e.getMessage(), e);
                    msg.recycle();
                    return false;
                }
    
                msg.markInUse();
                msg.when = when;
                Message p = mMessages;
                boolean needWake;
                if (p == null || when == 0 || when < p.when) {
                    //p为null(代表MessageQueue没有消息) 或者msg的触发时间是队列中最早的, 则进入该该分支
                    msg.next = p;
                    mMessages = msg;
                    needWake = mBlocked;
                } else {
                //将消息按时间顺序插入到MessageQueue。一般地,不需要唤醒事件队列,除非
                //消息队头存在barrier,并且同时Message是队列中最早的异步消息。
                    needWake = mBlocked && p.target == null && msg.isAsynchronous();
                    Message prev;
                    for (;;) {
                        prev = p;
                        p = p.next;
                        if (p == null || when < p.when) {
                            break;
                        }
                        if (needWake && p.isAsynchronous()) {
                            needWake = false;
                        }
                    }
                    msg.next = p; // invariant: p == prev.next
                    prev.next = msg;
                }
                if (needWake) {
                    nativeWake(mPtr);
                }
            }
            return true;
        }
    
    获取消息
    
    
    分发消息

    相关文章

      网友评论

          本文标题:Android 消息机制原理及源码学习

          本文链接:https://www.haomeiwen.com/subject/rfrbbxtx.html