从源码解析Handler机制

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 从源码解析Handler机制

Handler机制是面试中的常客了,今天和大家一起通过源码层面来解析一下。

@[toc]


前提知识点


Handler机制涉及到几个类: MessageQueue, Looper, Message, ActivityThread。

  • ActivityThread: 主线程,开启loop循环,管理application进程, 调度管理activity, 广播及其他操作。
  • Message: handler机制中消息的载体,包含相关描述和数据对象,包含属性what, obj, arg1,arg2等。
  • MessageQueue: 消息队列。
  • Looper:循环去MessageQueue中消息。

AppCompatActivity.java-> FragmentActivity.java->SupportActivity.java->Activity.java

在Activity中默认有一个ActivityThread这是一个main thread,其中final Looper mLooper = Looper.myLooper();实例化了一个looper对象。

在ActivityThread.java中的main方法中,有如下代码

public static void main(String[] args) {
           ...省略其他代码
        Looper.prepareMainLooper();
          ...省略其他代码
        Looper.loop();
    }
Looper.java
public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }
  //  Looper构造函数中初始了消息队列MessageQueue对象
private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

创建looper 对象之前,会判断 sThreaLocal 中是否已经绑定过 Looper 对象,如果是则抛出异常,确保一个线程中Looper.prepare()只调用一次。

如果在MainActivity中调用,如下代码,会报错。微信图片_20220610113225.png微信图片_20220610113237.png通过上述代码可以得知,Activity中默认开始了loop()循环,用于获取handler发送的消息。

最简单的应用


public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    private final String TAG = "MainActivity";
    public final int MSG_DOWN_FAIL = 1;
    public final int MSG_DOWN_SUCCESS = 2;
    public final int MSG_DOWN_START = 3;
    @BindView(R.id.btn_start_thread)
    Button btnStart;
    @BindView(R.id.tv_status)
    TextView tvShow;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_DOWN_START:
                    tvShow.setText("down start");
                    break;
                case MSG_DOWN_SUCCESS:
                    tvShow.setText("down success");
                    break;
                case MSG_DOWN_FAIL:
                    tvShow.setText("down fail");
                    break;
                default:
                    break;
            }
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        btnStart.setOnClickListener(this);
    }
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.btn_start_thread:
                new MyThread().start();
                break;
            default:
                break;
        }
    }
    class MyThread extends Thread {
        @Override
        public void run() {
            handler.sendEmptyMessage(MSG_DOWN_START);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            Message msg = Message.obtain();
            msg.what = MSG_DOWN_SUCCESS;
            handler.sendMessage(msg);
        }
    }
}

sendMessage之后发生了什么


public final boolean sendMessage(Message msg)
    {
        return sendMessageDelayed(msg, 0);
    }

sendMessage调用了sendMessageDelayed->sendMessageAtTime->enqueueMessage, 最终调用了MessageQueue的enqueueMessage的方法, 并将自己设置为message的target

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

接下来就到了消息队列MessageQueue中了,来看一下微信图片_20220610113357.png

  • 如果message的target为null,则直接抛出异常
  • 按照message的时间when有序插入到MessageQueue中,所以MessageQueue是一个按时间排序的有序队列。


怎么取MessageQueue中的消息


其实这里就是Looper.loop()方法从消息队列中不断循环取消息了。微信图片_20220610113436.png不断调用MessageQueue的next()方法取消息,如果message不为null, 则调用handler的dispatchMessage分发消息。微信图片_20220610113506.png微信图片_20220610113517.png这个handleMessage方法就是在创建Handler中覆盖的方法。

至此 Handler 的发送消息和消息处理流程已经介绍完毕。


面试常见问题


1.Looper.loop() 为什么不会阻塞主线程


Android的Ui线程,开启了一个死循环,但是并没有阻塞主线程是为什么呢?

在MessageQueue的next方法中有这样一行代码微信图片_20220610113549.png

private native void nativePollOnce(long ptr, int timeoutMillis); /*non-static for callbacks*/

nativePollOnce 方法是一个 native 方法,当调用此 native 方法时,主线程会释放 CPU 资源进入休眠状态,直到下条消息到达或者有事务发生,通过往 pipe 管道写端写入数据来唤醒主线程工作,这里采用的 epoll 机制。


2.Handler 的 sendMessageDelayed 或者 postDelayed 是如何实现的


messageQueue的enqueueMessage是按照消息的when时间有序加入到队列的,取的时候也是按照时间进行取的微信图片_20220610113626.png

可以看到如果当前时间小于msg设置的时间,会计算一个timeout,在timeout到了之后,才会将UI线程唤醒,交由CPU执行。


总结


1.APP启动创建主线程的时候会通过ActivityThread执行Looper.prepare(),创建一个looper 对象,在私有的构造方法中又创建了 MessageQueue 作为此 Looper 对象的成员变量,Looper 对象通过 ThreadLocal 绑定 MainThread 中。

2.在创建Handler对象的时候,通过构造函数获取ThreadLocal 绑定的looper对象,并通过looper获取消息队列MessageQueue作为成员变量

3.子线程发送消息时,将msg的target设置为handler自身,之后调用成员MessageQueue的enqueueMessage将消息按照msg.when时间排序插入到消息队列

4.主线程通过Looper.loop()开启不阻塞UI线程的死循环,通过绑定的looper对象获取MessageQueue,调用next()方法不断获取msg, 并通过(handler)msg.target.dispatchMessage发送至我们创建的handler时覆盖的handleMessage()方法

目录
相关文章
|
14天前
|
传感器 C# Android开发
深度解析Uno Platform中的事件处理机制与交互设计艺术:从理论到实践的全方位指南,助您构建响应迅速、交互流畅的跨平台应用
Uno Platform 是一款开源框架,支持使用 C# 和 XAML 开发跨平台原生 UI 应用,兼容 Windows、iOS、Android 及 WebAssembly。本文将介绍 Uno Platform 中高效的事件处理方法,并通过示例代码展示交互设计的核心原则与实践技巧,帮助提升应用的用户体验。事件处理让应用能响应用户输入,如点击、触摸及传感器数据变化。通过 XAML 或 C# 添加事件处理器,可确保及时反馈用户操作。示例代码展示了一个按钮点击事件处理过程。此外,还可运用动画和过渡效果进一步增强应用交互性。
127 57
|
21天前
|
存储 缓存 Java
什么是线程池?从底层源码入手,深度解析线程池的工作原理
本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。
什么是线程池?从底层源码入手,深度解析线程池的工作原理
|
21天前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
175 37
|
5天前
|
移动开发 Android开发 数据安全/隐私保护
移动应用与系统的技术演进:从开发到操作系统的全景解析随着智能手机和平板电脑的普及,移动应用(App)已成为人们日常生活中不可或缺的一部分。无论是社交、娱乐、购物还是办公,移动应用都扮演着重要的角色。而支撑这些应用运行的,正是功能强大且复杂的移动操作系统。本文将深入探讨移动应用的开发过程及其背后的操作系统机制,揭示这一领域的技术演进。
本文旨在提供关于移动应用与系统技术的全面概述,涵盖移动应用的开发生命周期、主要移动操作系统的特点以及它们之间的竞争关系。我们将探讨如何高效地开发移动应用,并分析iOS和Android两大主流操作系统的技术优势与局限。同时,本文还将讨论跨平台解决方案的兴起及其对移动开发领域的影响。通过这篇技术性文章,读者将获得对移动应用开发及操作系统深层理解的钥匙。
|
13天前
|
编解码 开发工具 UED
QT Widgets模块源码解析与实践
【9月更文挑战第20天】Qt Widgets 模块是 Qt 开发中至关重要的部分,提供了丰富的 GUI 组件,如按钮、文本框等,并支持布局管理、事件处理和窗口管理。这些组件基于信号与槽机制,实现灵活交互。通过对源码的解析及实践应用,可深入了解其类结构、布局管理和事件处理机制,掌握创建复杂 UI 界面的方法,提升开发效率和用户体验。
64 12
|
5天前
|
存储 关系型数据库 MySQL
深入解析MySQL数据存储机制:从表结构到物理存储
深入解析MySQL数据存储机制:从表结构到物理存储
14 1
|
9天前
|
Java 开发者
Java中的异常处理机制深度解析
在Java编程中,异常处理是保证程序稳定性和健壮性的重要手段。本文将深入探讨Java的异常处理机制,包括异常的分类、捕获与处理、自定义异常以及一些最佳实践。通过详细讲解和代码示例,帮助读者更好地理解和应用这一机制,提升代码质量。
12 1
|
15天前
|
存储 缓存 Android开发
Android RecyclerView 缓存机制深度解析与面试题
本文首发于公众号“AntDream”,详细解析了 `RecyclerView` 的缓存机制,包括多级缓存的原理与流程,并提供了常见面试题及答案。通过本文,你将深入了解 `RecyclerView` 的高性能秘诀,提升列表和网格的开发技能。
39 8
|
18天前
|
Java 程序员 开发者
Java中的异常处理机制深度解析
本文旨在深入探讨Java中异常处理的核心概念与实际应用,通过剖析异常的本质、分类、捕获及处理方法,揭示其在程序设计中的关键作用。不同于常规摘要,本文将直接切入主题,以简明扼要的方式概述异常处理的重要性及其在Java编程中的应用策略,引导读者快速把握异常处理的精髓。
|
17天前
|
安全 Java 开发者
Java并发编程中的锁机制解析
本文深入探讨了Java中用于管理多线程同步的关键工具——锁机制。通过分析synchronized关键字和ReentrantLock类等核心概念,揭示了它们在构建线程安全应用中的重要性。同时,文章还讨论了锁机制的高级特性,如公平性、类锁和对象锁的区别,以及锁的优化技术如锁粗化和锁消除。此外,指出了在高并发环境下锁竞争可能导致的问题,并提出了减少锁持有时间和使用无锁编程等策略来优化性能的建议。最后,强调了理解和正确使用Java锁机制对于开发高效、可靠并发应用程序的重要性。
16 3

热门文章

最新文章

推荐镜像

更多
下一篇
无影云桌面