要点提炼|开发艺术之消息机制

作者: 厘米姑娘 | 来源:发表于2017-12-23 22:36 被阅读1045次

    Android的消息机制指的是Handler的运行机制,本篇将总结Handler机制的相关知识点:

    • 消息机制概述
    • 消息机制分析

    1.消息机制概述

    a.作用:跨线程通信

    b.常用场景:当子线程中进行耗时操作后需要更新UI时,通过Handler将有关UI的操作切换到主线程中执行。

    系统不建议在子线程访问UI的原因:UI控件非线程安全,在多线程中并发访问可能会导致UI控件处于不可预期的状态。而不对UI控件的访问加上锁机制的原因有:

    • 上锁会让UI控件变得复杂和低效
    • 上锁后会阻塞某些进程的执行

    c.四要素:

    • Message(消息):需要被传递的消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,最终由Handler处理。
    • MessageQueue(消息队列):用来存放Handler发送过来的消息,内部通过单链表的数据结构来维护消息列表,等待Looper的抽取。
    • Handler(处理者):负责Message的发送及处理。
      • Handler.sendMessage():向消息池发送各种消息事件。
      • Handler.handleMessage() 处理相应的消息事件。
    • Looper(消息泵):通过Looper.loop()不断地从MessageQueue中抽取Message,按分发机制将消息分发给目标处理者。

    Thread(线程):负责调度整个消息循环,即消息循环的执行场所。

    存在关系:

    • 一个Thread只能有一个Looper,可以有多个Handler;
    • Looper有一个MessageQueue,可以处理来自多个Handler的Message;
    • MessageQueue有一组待处理的Message,这些Message可来自不同的Handler;
    • Message中记录了负责发送和处理消息的Handler;
    • Handler中有Looper和MessageQueue;
    关系 数量关系

    图片来源android的消息处理机制之Looper,Handler,Message

    d.实现方法:

    • 在主线程实例化一个全局的Handler对象;
    • 在需要执行UI操作的子线程里实例化一个Message并填充必要数据,调用Handler.sendMessage(Message msg)方法发送出去;
    • 复写handleMessage()方法,对不同Message执行相关操作;

    实例:Service篇--异步消息处理机制


    2.消息机制分析

    a.工作流程:

    • Handler.sendMessage()发送消息时,会通过MessageQueue.enqueueMessage()向MessageQueue中添加一条消息;
    • 通过Looper.loop()开启循环后,不断轮询调用MessageQueue.next()
    • 调用目标Handler.dispatchMessage()去传递消息,目标Handler收到消息后调用Handler.handlerMessage()处理消息。

    简单来看,即HandlerMessage发送到Looper的成员变量MessageQueue中,之后Looper不断循环遍历MessageQueue从中读取Message,最终回调给Handler处理。如图:

    b.工作原理:

    • (1)Looper的创建:先从应用程序的入口函数ActivityThread.main()看起,在这里(主线程)系统自动创建了Looper,主要方法:
    //主线程中不需要自己创建Looper
    public static void main(String[] args) {
            ......
            Looper.prepareMainLooper();//为主线程创建Looper,该方法内部又调用 Looper.prepare()
            ......
            Looper.loop();//开启消息轮询
            ......
            
        }
    

    注意

    • 子线程的Looper需要手动去创建,标准写法是:
    //子线程中需要自己创建一个Looper
    new Thread(new Runnable() {
                @Override
                public void run() {
                    
                    Looper.prepare();//为子线程创建Looper               
                    Looper.loop(); //开启消息轮询
                }
            }).start();
    
    • 无论是主线程还是子线程,Looper只能被创建一次,即一个Thread只有一个Looper。
    • 所创建的Looper会保存在ThreadLocal(线程本地存储区)中,它不是线程,作用是帮助Handler获得当前线程的Looper。更多讲解见ThreadLocal详解
    • (2)MessageQueue的创建:在Looper的构造函数创建了一个MessageQueue:
     private Looper(boolean quitAllowed) {
           
            mQueue = new MessageQueue(quitAllowed);
            mThread = Thread.currentThread();
        }
    
    • (3)Message轮询及处理:有了Looper和MessageQueue之后通过Looper.loop()开启消息轮询
    public static void loop() {
            ......
            for (;;) {//死循环
                Message msg = queue.next(); //用于提取下一条信息,该方法里同样有个for(;;)死循环,当没有可处理该Message的Handler时,会一直阻塞
                if (msg == null) {
                    
                    return;
                }
             ......   
             try {
                    msg.target.dispatchMessage(msg);//如果从MessageQueue中拿到Message,由和它绑定的Handler(msg.target)将它发送到MessageQueue
                    end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
                } finally {
                    if (traceTag != 0) {
                        Trace.traceEnd(traceTag);
                    }
                }
             ......   
        }
    
    
    • 现在就剩创建Handler及Message发送了。(4)先看Handler的创建:有两种形式的Handler:
    //第一种:send方式的Handler创建
    Handler handler = new Handler() {
                @Override
                public void handleMessage(Message msg) {
                    //如UI操作
                    
                }
            };
    
    //第二种:post方式的Handler创建
    Handler handler = new Handler();
    

    注意:创建Handler实例之前必须先创建Looper实例,否则会抛RuntimeException。

    • (5) Message的发送:

    对于send方式的Handler:创建好一个Message后,调用Handler的以下几种常见的方法来发送消息:

    sendEmptyMessage();           //发送空消息
    sendEmptyMessageAtTime();     //发送按照指定时间处理的空消息
    sendEmptyMessageDelayed();    //发送延迟指定时间处理的空消息
    sendMessage();                //发送一条消息
    sendMessageAtTime();          //发送按照指定时间处理的消息
    sendMessageDelayed();         //发送延迟指定时间处理的消息
    sendMessageAtFrontOfQueue();  //将消息发送到消息队头
    

    对于post方式的Handler,可在子线程直接调用Handler的以下几种常见方法,使得切换到主线程:

    post(Runnable r)
    postAtFrontOfQueue(Runnable r)
    postAtTime(Runnable r, Object token, long uptimeMillis)
    postAtTime(Runnable r, long uptimeMillis)
    postDelayed(Runnable r, long delayMillis)
    
    //例如,postDelayed()方法
    handler.postDelayed(new Runnable() {
                        @Override
                        public void run() {
                            //如UI操作
                        }
                    },300);
    
    

    通过以上各种Handler的发送方法,都会依次调用 Handler.sendMessageDelayed->Handler.sendMessageAtTime()->MessageQueue.enqueueMessage() 最终将Message发送到MessageQueue。

    至此从源码已走过一遍流程。

    推荐阅读:深入了解Android的消息机制(源码)

    最后,将Handler机制汇总到一张图:

    图片来源android的消息机制——Handler机制

    现在,这里有一些有关消息机制的Questions,考考自己吧!


    希望这篇文章对你有帮助~

    相关文章

      网友评论

      • DawnT_Young:请问一下,你的流程图都是用什么工具画的呢
        厘米姑娘:@DawnT_Young |・ω・`)哈哈我挑图的时候也很看重是否清晰易理解,同道中人呀
        DawnT_Young:@minmin_1123 感谢回复,你太谦虚了,看过很多博客,流程图做的这么好看的很少:grin:
        厘米姑娘:@DawnT_Young _(:з」∠)_有些图不是我的,我有标注文章来源,我平常用photoshop还有presson,很业余的那种

      本文标题:要点提炼|开发艺术之消息机制

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