Handler消息机制详解

本文详细剖析了Android中的Handler消息机制,涵盖Handler、MessageQueue、Looper和ThreadLocal的概念及工作原理。通过问答形式,解释了为何不能在子线程中访问UI、Handler的运行机制、如何在子线程中发送消息到主线程等问题。同时,文章探讨了ThreadLocal的作用及其在消息机制中的应用,以及如何避免Handler引发的内存泄漏问题。
摘要由CSDN通过智能技术生成

转载请注明链接:https://blog.csdn.net/feather_wch/article/details/79263855

详解Handler消息机制,包括Handler、MessageQueue、Looper和LocalThread。

本文是我一点点归纳总结的干货,但是难免有疏忽和遗漏,希望不吝赐教。

Handler消息机制详解(29题)

版本:2018/9/7-1(2359)


问题汇总

以问答形式将本文的所有内容进行汇总。考考你哟。

  1. Handler是什么?
  2. 消息机制是什么?
  3. MeesageQueue是什么?
  4. Looper是什么?
  5. ThreadLocal是什么?
  6. 为什么不能在子线程中访问UI?
  7. Handler的运行机制概述(Handler的创建、Looper的创建、MessageQueue的创建、以及消息的接收和处理)
  8. 主线程向子线程发送消息的方法?
  9. ThreadLocal的作用?
  10. ThreadLocal的应用场景
  11. ThreadLocal的使用
  12. ThreadLocal的set()源码分析
  13. ThreadLocal的get()源码分析
  14. MessageQueue的主要操作
  15. MessageQueue的插入和读取源码分析
  16. MessageQueue的next源码详解
  17. Looper的构造中做了什么?
  18. 子线程中如何创建Looper?
  19. 主线程ActivityThread中的Looper是如何创建和获取的?
  20. Looper如何退出?
  21. quit和quitSafely的区别
  22. Looper的loop()源码中的4个工作?
  23. Android如何保证一个线程最多只能有一个Looper?
  24. Looper的构造器为什么是private的?
  25. Handler消息机制中,一个looper是如何区分多个Handler的?
  26. 主线程ActivityThread的消息循环
  27. ActivityThread中Handler H是做什么的?
  28. Handler的post/send()逻辑流程
  29. Handler的postDelayed逻辑流程
  30. Handler的消息处理的流程?
  31. Handler的特殊构造方法中,为什么有Callback接口?
  32. Handler的内存泄漏如何避免?

消息机制概述(8)

1、Handler是什么?

  1. Android消息机制的上层接口(从开发角度)
  2. 能轻松将任务切换到Handler所在的线程中去执行
  3. 主要目的在于解决在子线程中无法访问UI的矛盾

2、消息机制?

  1. Android的消息机制主要就是指Handler的运行机制
  2. Handler的运行需要底层MessageQueueLooper的支撑

3、MeesageQueue是什么?

  1. 消息队列
  2. 内部存储结构并不是真正的队列,而是单链表的数据结构来存储消息列表
  3. 只能存储消息,而不能处理

4、Looper是什么?

  1. 消息循环
  2. Looper以无限循环的形式去查找是否有新消息,有就处理消息,没有就一直等待着。

5、ThreadLocal是什么?

  1. Looper中一种特殊的概念
  2. ThreadLocal并不是线程,作用是可以在每个线程中互不干扰的存储数据提供数据
  3. Handler创建时会采用当前线程的Looper来构造消息循环系统,Handler内部就是通过ThreadLocal来获取当前线程的Looper
  4. 线程默认是没有Looper的,如果需要使用Handler就必须为线程创建Looper
  5. UI线程就是ActivityThread,被创建时会初始化Looper,因此UI线程中默认是可以使用Handler

6、为什么不能在子线程中访问UI?

ViewRootImpl会对UI操作进行验证,禁止在子线程中访问UI:

void checkThread(){
  if(mThread != Thread.currentThread()){
    throw new CalledFromWrongThreadException("Only th original thread that created a view hierarchy can touch its views");
  }
}

7、Handler的运行机制概述(Handler的创建、Looper的创建、MessageQueue的创建、以及消息的接收和处理)

  1. Handler创建时会采用当前线程的Looper
  2. 如果当前线程没有Looper就会报错,要么创建Looper,要么在有Looper的线程中创建Handler
  3. Handlerpost方法会将一个Runnable投递到Handler内部的Looper中处理(本质也是通过send方法完成)
  4. Handlersend方法被调用时,会调用MessageQueueenqueueMessage方法将消息放入消息队列, 然后Looper发现有新消息到来时,就会处理这个消息,最终消息中的Runnable或者HandlerhandleMessage就会被调用
  5. 因为Looper是运行在创建Handler所在的线程中的,所以通过Handler执行的业务逻辑就会被切换到Looper所在的线程中执行。

8、主线程向子线程发送消息的方法?

  1. 通过在主线程调用子线程中Handler的post方法,完成消息的投递。
  2. 通过HandlerThread实现该需求。

ThreadLocal(4)

1、ThreadLocal的作用

  1. ThreadLocal是线程内部的数据存储类,可以在指定线程中存储数据,之后只有在指定线程中才开业读取到存储的数据
  2. 应用场景1:某些数据是以线程为作用域,并且不同线程具有不同的数据副本的时候。ThreadLocal可以轻松实现Looper在线程中的存取。
  3. 应用场景2:在复杂逻辑下的对象传递,通过ThreadLocal可以让对象成为线程内的全局对象,线程内部通过get就可以获取。

2、ThreadLocal的使用

mBooleanThreadLocal.set(true);
Log.d("ThreadLocal", "[Thread#main]" + mBooleanThreadLocal.get());

new Thread("Thread#1"){
    @Override
    public void run(){
        mBooleanThreadLocal.set(false);
        Log.d("ThreadLocal", "[Thread#1]" + mBooleanThreadLocal.get());
    }
}.start();

new Thread("Thread#2"){
    @Override
    public void run(){
        Log.d("ThreadLocal", "[Thread#2]" + mBooleanThreadLocal.get());
    }
}.start();
  1. 最终main中输出true; Thread#1中输出false; Thread#2中输出null
  2. ThreadLocal内部会从各自线程中取出数组,再根据当前ThreadLocal的索引去查找出对应的value值。

3、ThreadLocal的set()源码分析

//ThreadLocal.java
public void set(T value) {
    //1. 获取当前线程
    Thread t = Thread.currentThread();
    //2. 获取当前线程对应的ThreadLocalMap
    ThreadLocalMap map = getMap(t);
    if (map != null)
        //3. map存在就进行存储
        map.set(this, value);
    else
        //4. 不存在就创建map并且存储
        createMap(t, value);
}
//ThreadLocal.java内部类: ThreadLocalMap
private void set(ThreadLocal<?> key, Object value) {
    //1. table为Entry数组
    Entry[] tab = table;
    int len = tab.length;
    int i = key.threadLocalHashCode & (len-1);
    //2. 根据当前ThreadLocal获取到Hash key,并以此从table中查询出Entry
    for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
        ThreadLocal<?> k = e.get();
        
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值