关于 Handler 的灵魂三问

栏目: 后端 · 发布时间: 5年前

内容简介:之前在郭神的订阅号,看到了这一篇关于介绍得很详细,分析源码的流程也很清晰。

之前在郭神的订阅号,看到了这一篇关于 Handler 的投稿文章

《或许你可以从这个角度去理解Handler》

介绍得很详细,分析源码的流程也很清晰。

Message 的对象获取方式,到 HandlersendMessage 方法解析,再到 enqueueMessage 方法解析。

enqueueMessage 中没有看到回调方法 handleMessage ,结果是 dispatchMessage 方法调用了 handleMessage

再看 HandlerLooper 类的关系,又发现 loop 方法中调用了 dispatchMessage ,最后分析了 loop 方法,得出结论, MessageQueue 消息队列最后是在这个方法执行的。

这位作者大佬也是事无巨细,在重要的方法和代码处都加了注释,使得读者理解起来更轻松,值得学习。

但由于个人理解能力有限,部分地方还存在疑惑,于是就自己动手丰衣足食,结合面试中可能会问到的一些问题来做了学习。

于是有了此文。

关于 Handler 的灵魂三问

这是重点

1.Handler 的原理?

面试官最爱问的一个问题

首先我们了解一下

Handler —— 处理者

Message —— 消息

MessageQueue —— 消息队列

Looper —— 循环者

于是,我们大概可以这样描述:

消息处理者 Handler 从子线程中发送消息 Meesage 到 消息队列 MessageQueue 中,消息队列 MessageQueue 会对消息进行排序,循环者 Looper 循环地从消息队列 MessageQueue 中取出消息,回调给主线程中的消息处理者 Handler , 在 handleMessage 方法处理结果。完成一次消息异步发送的流程。

然后,走一遍流程:

  • 回调方法,在此处理你要做的事情
public Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        //TODO..
    }
}
复制代码
  • 发送消息
handler.sendMessage(m);

handler.post(r);
复制代码
  • 发送消息最终调用的是这个方法
queue.enqueueMessage(msg, uptimeMillis);
复制代码
  • 消息队列 MessageQueue 中入队方法 enqueueMessage() ,出队方法 next()
boolean enqueueMessage(Message msg, long when) {...

Message next() {...
复制代码

next() 在循环者 Looperloop 方法中被调用

获取到消息后,调用 dispatchMessage(msg) 来分发消息

public static void loop() {...

msg.target.dispatchMessage(msg);
...
复制代码

最终回到了 Handler 类中的 dispatchMessage 方法,如果 Message 是一个纯粹的消息体,那将会调用 handleMessage 方法。

public void dispatchMessage(Message msg) {
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        handleMessage(msg);
    }
}
复制代码

到此,走完了这次流程。

我想你大概能知道,如何组织自己的语言回答面试官了。

但,可能多数面试官不止于此。

继续问道...

2.Handler 为什么可以 post Runnable?

这个问题,不太难理解,Ctrl+鼠标左键,看过源码的都能知道。我们跟着源码走一遍

handler.post(new Runnable() {...
复制代码

这里实际调用了 sendMessageDelayed(Message msg, long delayMillis)Runnable 转成了 Message 对象,如何转换的呢

public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}
复制代码

可以看到 Message 中的 callback 属性是一个 Runnable 对象

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    m.callback = r;
    return m;
}
复制代码

3.Loop 死循环为什么不会导致主线程 ANR?

这个问题本身可能是有问题的...

  • 首先我们要知道 loop 方法中为什么要用死循环?

先说 ActivityThread ,虽然类名以 Thread 结尾,但它并没有继承 Thread ,它并不是一个线程类。

ActivityThreadAndroid 应用程序的入口,也就是任何一个进程的主线程入口。

public static void main(String[] args) {
    ...
    
    Looper.prepareMainLooper();

    ActivityThread thread = new ActivityThread();
    thread.attach(false);

    if (sMainThreadHandler == null) {
        sMainThreadHandler = thread.getHandler();
    }
    
    Looper.loop();

    throw new RuntimeException("Main thread loop unexpectedly exited");
复制代码

这就是一个 Java 应用, main 函数是入口,当执行到 loop 方法就开始死循环,如果死循环结束,那将会报错。

可以看到就是这样设计的。目的就是为了我们点击应用的图标时不会一闪而过,而是正确地打开页面,执行生命周期,响应各种输入事件。

我们的代码就是在主线程 loop 函数中的死循环中被执行的,所以这和 ANR 是两个不同的东西。

至于 Looper 会被阻塞,这个更深入一些,不在这里讨论。

  • 什么情况下会发生ANR?

1.输入事件(按键和触摸事件)5s内没被处理。

2. BroadcastReceiver 的事件(onRecieve方法)在规定时间内没处理完(前台广播为10s,后台广播为60s)。

3. service 前台20s后台200s未完成启动。

4. ContentProviderpublish 在10s内没进行完。

所以它不包括 Looper.loop() 的死循环。

写在后面

一直想总结一下自己对 Handler 的认识,以增强记忆,帮助学习和理解。

直到今天才完成。

关于 Handler ,它实现了线程之间的通信,而线程只是CPU调度的最小单位。了解了 Handler 的原理只是看到冰山一角。要想了解更多的通信方式,还有很长的路要走。

Linux已经拥有的进程间通信IPC手段包括(Internet Process Connection): 管道(Pipe)、信号(Signal)和跟踪(Trace)、插口(Socket)、报文队列(Message)、共享内存(Share Memory)和信号量(Semaphore)。而 Binder 是主要的IPC方式。

记录在此,仅为学习!

感谢您的阅读!欢迎指正!

参考:

1. yanzhenjie.blog.csdn.net/article/det…

2. www.zhihu.com/question/34…

3. www.jianshu.com/p/fa962a5fd…


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

编写高质量代码

编写高质量代码

秦小波 / 机械工业出版社华章公司 / 2011-12-28 / 59.00元

在通往“Java技术殿堂”的路上,本书将为你指点迷津!内容全部由Java编码的最佳实践组成,从语法、程序设计和架构、工具和框架、编码风格和编程思想等五大方面对Java程序员遇到的各种棘手的疑难问题给出了经验性的解决方案,为Java程序员如何编写高质量的Java代码提出了151条极为宝贵的建议。对于每一个问题,不仅以建议的方式从正反两面给出了被实践证明为十分优秀的解决方案和非常糟糕的解决方案,而且还......一起来看看 《编写高质量代码》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具