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

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)将它发送到handleMessage处理
                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,考考自己吧!


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

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

推荐阅读更多精彩内容