android Handler消息处理源码剖析

1、什么是Handler

A Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it --from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.

Handler允许你发送和处理Message(消息)/Runnable对象到当前线程的MessageQueue(消息队列)中。每个Handler实体对象都和当前所处线程的一个消息队列进行关联。当你创建一个新的Handler,这个Handler就和当前所处的线程和消息队列绑定在一起,从这个时刻开始,该Handler发送消息和Runnable对象到消息队列,在从MessageQueue取出消息的那一刻就开始执行消息。

2、Handler可以做什么

Handler是Android的一种消息处理机制,这种机制主要是为了解决Android应用中多线程的问题:在Android中不允许Activity新启动的线程访问该Activity里的UI组件,这样会导致新启动的线程无法改变UI组件的属性值。但实际开发中,很多地方需要在工作线程中改变UI组件的属性值,比如下载网络图片、动画等等。

Handler有两个作用:

(1)在工作线程中发送消息

(2)在UI线程中取出和处理消息

3、Handler提供的API

Handler提供两种方式将消息发送的队列:post和sendMessage。

Post:Post允许把一个Runnable对象加入到MessageQueue中,方法有post、postAtTime、postDelayed。

SendMessage:sendMessage允许把一个包含消息数据的Message放入MessageQueue中。包含sendEmptyMessage、sendMessage、sendMessageAtTime、sendMessageDelayed。

4、支撑Handler机制的架构图

架构图

可以看到支撑Handler消息处理机制需要至少Handler、Message、MessageQueue、Looper四个类。

5、Handler、MessageQueue、Looper、Message的类图关系

类图关系

Handler:消息的真正处理类,具有发送消息、处理消息、移除消息的功能。依赖于MessageQueue和Looper;mQueue是MessageQqeue消息队列,通过sendMessage或者post方法发送的Message就存放在这个队列里面。mLooper是Looper消息调度器,用于循环取出mQueue中的Message。

MessageQueue:mMessages存放了所有的Message,以链表的形式存储。

Looper:用于循环从MessageQueue中取得Message,然后把、执行Message对应target的dispatchMessage方法。依赖于MessageQueue;mThreadLocal用于存放当前线程对应的Looper;sMainLooper表示UI主线程的Looper;mQueue是所有消息的队列。

Message:代表一条消息,依赖于Handler进行消息处理,对应一个Handler类型的target,在加入队列之前就会进行target设置。

6、Message消息从发送到运行源码解析

handler使用示例

通常情况下,我们通过创建一个Handler和一个Message,然后调用sendMessage来发送消息。流程图如下:

发送Message序列图

在创建Handler的时候,会触发Handler如下构造函数,该构造函数首先调用Looper.myLooper()创建一个Looper对象。

new Handler()

Looper.myLooper()实现如下,myLoop方法取得当前线程保持的Looper对象。

Looper.myLooper()

myLooper()如果返回空值,在创建Handler时就会直接crash。因此在创建Handler时需要先创建Looper对象,并设置到Looper的sThreadLocal,;sTheadLocal用来保存当前线程的Looper对象,那当前线程的Looper对象是什么时候设置的呢?跟踪代码发现Looper.prepare方法会创建一个Looper,Looper对象在创建时会关联一个mQueue对象,最后将Looper设置到ThreadLocal,从此每个线程就会有自己的Looper对象。

Looper.prepare()
Looper构造函数

因此在创建Handler之前需要先调用Looper.prepare()为当前线程创建一个Looper对象。创建完Looper对象后,就将该Looper的MessageQueue对象赋值给Handler,这样sendMessage()发送的Message就直接交给Looper的MessageQueue对象。

那么问题来了,当一个Message放入MessageQueue中后,谁来运行它呢?又是什么时机来运行的呢?我们似乎没有看到从MessageQueue取Message并运行的流程。上面我们提到了Looper类是不停地循环取Message的帮助类;没错,Android内部通过Looper的loop方法来不停地遍历MessageQueue。代码如下:

Looper.loop()

当调用了Looper.loop方法后,首先通过myLooper()方法取得当前线程的Looper对象,也就是sThreadLocal中保存的值;然后取得当前线程Looper对象的mQueue,最后通过无线循环在取出mQueue的Message;运行Message对应target(Handler)的dispatchMessage方法。

因此,要实现一个Handler的正确做法应该如下:

正确使用Handler的方法

在loop()调用后会运行每个message.target.dispatchMessage()方法,那target是在什么时候设置的呢?在调用Handler的enqueueMessage方法时就会将自身设置给Message对象。

sendMessage最终调用的方法

这样当Looper.loop()取得Message后就会直接调用handler的dispatchMessage(),该函数其实就是调用handleMessage方法,也就是我们在创建Handler时需要自己实现的方法。

handler最终执行Message的方法

如果用户在创建Message的时候传入了Callback就调用Message的Callback,Callback是一个Rnnable接口。如果没有传入Callback,就查看当前Handler在创建时是否指定了Callback,Handler的Callback只是一个handleMessage接口;如果都没有直接调用handler的handMessage()方法,这就是我们每次创建Handler都需要自己实现handlerMessage()的原因。

上面提到了在创建Handler时需要先调用Looper.prepare(),但是我们在实际编写代码中,例如在Activity中创建Handler没有主动调用Looper.prepare(),却没有报"Can't create handler inside thread that has not called

Looper.prepare()",这是因为Activity属于UI主线程,在APK启动时系统已经帮我们建好Looper了,代码就在AvtivityThread中,ActivityThread的main函数是整个APK运行的入口如下所示:

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

推荐阅读更多精彩内容