Android消息机制

通常说到Android的消息机制,一般也是说的Android的Handler的运行机制,而Hander的运行过程,可以说是我们App项目运行的一个基础,app运行过程中的很多事件,如Activity的生命周期回调,线程之间的通信,都是需要通过Handler来处理的。

这里有一张Handler的运行模型图。


1.以Handler的sendMessage方法为例,当发送一个消息后,会将此消息加入消息队列MessageQueue中。

2.Looper负责去遍历消息队列并且将队列中的消息分发给对应的Handler进行处理。

3.在Handler的handleMessage方法中处理该消息,这就完成了一个消息的发送和处理过程。

这里从图中可以看到参与消息处理有四个对象,它们分别是Handler, Message, MessageQueue,Looper。

现在来看一下Handler的前世今生

1 当我们当App启动的时候,首先就会进入ActivityThread的main方法里面,这里也是我们Android程序的入口。

在这个main方法里面,我们会创建app中全局唯一的Looper对象和MessageQueue对象。

这些对象都创建好之后,我们的looper对象就开始进入loop状态,就是无限循环状态,不停的从Message Queue中获取Message对象,如果有需要处理的Message对象,就交给Handler对象处理,如果没有,那就把自己阻塞起,一直等到有新的Message对象被添加到MessageQueue中才被唤醒。

2 当我们需要在自己的应用中使用Handler的时候,我们可以之间使用UI线程中的Handler对象,也可以继承Handler对象,重写handlerMessage方法。因为我们的Looper对象是全局对象,生命周期和app一样,所以在使用的时候,这里需要注意避免因为Handler的使用不当造成内存泄露。这里推荐使用内部静态类加若引用的方式。


3 使用Handler发送消息,不管Handler是在哪里发送消息,最后消息都会被放入到我们的全局消息队列里面,等待处理。


4最后是处理消息。Looper会不停的从消息队列里面去取Message对象。然后调用handler的dispatchMessage方法,将消息丢给Handler的handlerMessage方法处理。



以上就是Handler的一个工作流程。从Handler创建,到发送消息到消息队列,然后Looper不断的循环从消息队列中再把消息取出来,最后再交给Handler的handlerMessage方法处理。通过这样的一个循环反复,来处理我们App里面的相关事件。


消息阻塞和延时

Looper 的阻塞主要是靠MessageQueue 来实现的,在next()进行阻塞,在enqueueMessage进行唤醒。主要依赖native 层的Looper 依靠epoll 机制进行的。

阻塞和延时,主要是next()中nativePollOnce(ptr, nextPollTimeoutMillis)调用naive方法操作管道,由nextPollTimeoutMillis决定是否需要阻塞nextPollTimeoutMillis为0的时候表示不阻塞,为-1的时候表示一直阻塞直到被唤醒,其他时间表示延时。


唤醒

主要是指enqueueMessage()@MessageQueue 进行唤醒。


简单理解阻塞和唤醒

就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。

这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

常见问题分析

1 为什么不能在子线程中更新UI,根本原因是什么?

mThread是UI线程,这里会检查当前线程是不是UI线程。那么为什么onCreate里面没有进行这个检查呢。这个问题原因出现在Activity的生命周期中,在onCreate方法中,UI处于创建过程,对用户来说界面还不可视,直到onStart方法后界面可视了,再到onResume方法后界面可以交互。在Android中,UI不是线程安全的,因为是线程不安全的,所以当UI在主线程中被创建出来后,就只能在Ui线程中进行操作来。否则会出现数据不同步的问题。所以是不可以在自线程中操作UI的。那为什么要设计成线程不安全的呢,这里是为了提高UI的执行效率。

2为什么主线程用Looper死循环不会引发ANR异常?

简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,

此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,

通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制。


3为什么Handler构造方法里面的Looper不是直接new

如果在Handler构造方法里面new Looper,怕是无法保证保证Looper唯一,只有用Looper.prepare()才能保证唯一性,具体去看prepare方法。


4MessageQueue为什么要放在Looper私有构造方法初始化?

因为一个线程只绑定一个Looper,所以在Looper构造方法里面初始化就可以保证mQueue也是唯一的Thread对应一个Looper 对应一个mQueue。

5 Handler.post的逻辑在哪个线程执行的,是由Looper所在线程还是Handler所在线程决定的?

由Looper所在线程决定的。逻辑是在Looper.loop()方法中,从MsgQueue中拿出msg,并且执行其逻辑,这是在Looper中执行的,因此有Looper所在线程决定。

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