深入理解Handler机制之引路篇

要想深入理解Handler机制,就要理解:

  • Android为何要引入Handler机制?
  • Handler机制究竟是什么?

如果去查阅官方Handler文档,会发现官方没有给Handler下过定义,更没有介绍为何要引入Handler机制。官方文档的第一句就说Handler允许你发送和处理与一个线程的消息队列关联的消息和任务。这是对Handler能力和用法的一种描述,并没有讲为什么和是什么,这就造成初学者只知道怎么用Handler,但是不知道其背后的设计思想和原理。

Android为何引入Handler

要知道Android为何引入Handler机制,首先要了解Java线程机制:

  • 线程执行任务
  • 任务在线程的run方法中执行
  • run方法结束,线程终止

这种机制的问题在于,创建线程的开销非常大,即使通过线程池重用线程,线程调度的开销对于数量多、密度大、要求快速响应的UI更新任务来说也是不可接受的,于是Android引入Handler线程解决这个问题。Handler线程就是Handler机制的核心:Handler线程永不退出,在run方法中轮询任务,有任务就处理,没任务就等待。

有些Handler线程是允许退出的。

Android应用的主线程是一个不允许退出、生命周期与进程一样长的线程。

Handler机制工作原理

Handler机制是一整套任务处理机制,需要Handler、Looper、Message和MessageQueue配合。

  • Looper在线程中死循环,使线程不退出
  • Handler发送和处理Message
  • Message包含任务或者执行任务所需的数据
  • Message发送给MessageQueue,MessageQueue负责Message的排序与出队

Message是Android中的一个重要概念,它把Android应用从Java的任务处理系统变成了消息处理系统。

Handler机制工作原理

Looper

要把一个线程变成Handler线程,只需在线程的run方法中调用Looper.prepare()和Looper.loop()。

class LooperThread extends Thread {
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
}

Looper的创建非常巧妙,是在Looper.prepare()中完成的。Looper实例存储在ThreadLocal中,可以通过Looper.myLooper()获取。Looper默认是可以退出的,要创建不可退出的Looper,需要调用Looper.prepare(false),主线程Looper正是这样创建的。要注意的是,一个线程只能与一个Looper关联。

Looper创建后通过Looper.loop()完成工作。正如上文所说,Looper.loop()在Thread.run方法中轮询消息,使线程不退出、消息串行处理。

// Looper.java
public static void loop() {
    for (;;) {
        Message msg = queue.next(); // might block
        msg.target.dispatchMessage(msg);
    }
}

MessageQueue

MessageQueue是在Looper的构造函数内创建的。由此可知,线程、Looper和MessageQueue三者是一一对应的。

MessageQueue的主要工作是处理Message入列和安排Message出列。MessageQueue并不是常规的先进先出队列,而是根据Message期望执行的时间排列的。

  • 入列:boolean enqueueMessage(Message msg, long when)
  • 出列:Message next()

这两个方法都有用到native代码,实现比较复杂,就不分析了(因为没看懂😄)。

Message

Message是一个数据类,携带要执行的任务(Runnable)或执行任务所需的数据(what、arg1、arg2、obj、data)以及任务处理者(Handler)和任务执行时间(when)。Message的实现是一个单链表结点,因此可以携带下一条Message。MessageQueue正是基于Message单链表实现的。

由于是数据类,且应用中需要大量Message,因此Message实现了重用缓存池,通过Message.obtain()获取。与MessageQueue一样,Message缓存池也是基于其单链表实现的,不同的是缓存池是栈而非队列。

Handler

官方文档说,Handler与创建它的线程及该线程的MessageQueue关联(When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it)。这说的是不指定Looper的情况下,Handler自动与该线程的Looper关联,上文说,线程、Looper和MessageQueue三者一一对应,因此默认情况下,Handler与创建它的线程及该线程的MessageQueue关联。但在创建Handler时是可以指定Looper的,这样就会与Looper所在的线程和MessageQueue关联。

不管Handler在哪个线程创建,它发出的消息都是在与之关联的Looper所在的线程处理的,这正是Handler用于线程间通信的原理。由于后台线程不允许更新UI,因此后台线程通过一个与主线程Looper关联的Handler(new Handler(Looper.getMainLooper()))来更新UI。

Handler发送消息有两种方式:

  • sendMessage(Message)
  • post(Runnable)

但其实就一种,因为Runnable也会转为Message。Handler发送消息还可以设置延时或定时,但最终也是一种,因为延时也会被转为定时任务。因此所有的发送方式最终都调用下列方法处理:

// Handler.java
public boolean sendMessageAtTime(Message msg, long uptimeMillis)

放入MessageQueue中的Message会被逐个取出,串行处理。有3处对象可以处理消息:

  1. Message自身callback(一般是post(Runnable)方式发送的消息)
  2. Handler创建时设置的mCallback
  3. Handler
// Handler.java
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}

HandlerThread

Android应用的大部分消息都可以由主线程处理,如果需要在后台线程中处理消息,可以创建一个新的Handler线程。

开发者并不需要自己实现Handler线程,直接使用内置的HandlerThread即可:

HandlerThread handlerThread = new HandlerThread("MyHandlerThread");
handlerThread.start();
Handler handler = new Handler(handlerThread.getLooper());

源码篇和实现篇

要想深入理解Handler机制,光凭一篇引路文章是不够的,更重要的是阅读源码、动手实践和总结分享。

是不是很期待源码篇和实践篇?你来写!

看好你哦~

拓展阅读

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

推荐阅读更多精彩内容