第二节课_Handler|青训营笔记

简介: 在通常的情况下,我们会在子线程处理耗时操作,等待子线程耗时操作结束之后,再通知主线程更新UI。

线程通信_Handler

Handler 作用

在通常的情况下,我们会在子线程处理耗时操作,等待子线程耗时操作结束之后,再通知主线程更新UI。

Handler 是运行在主线程中的,它的作用就是帮助我们在子线程和主线程之间进行线程通信。我们可以在子线程中发送消息通知主线程,然后主线程中监听接收消息,进而对该消息进行处理。

1.webp.jpg

初识 Handler 的话,我们需要记住的是,他是一个不断在 looper 之中不断轮询的一个消息队列。我们可以不断的往这个轮询的池子中丢入信息,然后在获取的时候就是不断从池子里面的接受信息。

其基本用法也就是 发送接收

android.os.Handler handler = new Handler(Looper.getMainLooper()){
  @Override
  public void handleMessage(final Message msg) {
    //这里接受并处理消息
  }
};
//发送消息
handler.sendMessage(message);
handler.post(runnable);
复制代码
//简单示意
//子线程发消息
Thread {
            val msg = Message()
            msg.what = 1
            handler?.handleMessage(msg)
        }.start()
//主线程接收消息
handler = object : Handler(Looper.getMainLooper()){
            override fun handleMessage(message: Message){
                super.handleMessage(message)
                if (message.what == 1){
                    val TAG = "hhh"
                    Log.d(TAG, "handleMessage: ${message.what}")
                }
            }
        }
复制代码

Handler 浅析

  • Handler 与线程如何关联?
    Handler 在创建的时候是要先创建 Looper,就如上边的示意代码,创建 Handler 的时候,得传入一个 Looper 类型到构造器。

而Looper 使用 Looper.prepare() 方法来创建 Looper ,并借助 ThreadLocal 来实现与当前线程的绑定功能。

  • Handler 发出去的消息是谁管理的?
    我们查看 sendMessage(message) 或者 post(runnable) 可以得知,最后他们都会调用 MessageQueue.enqueueMessage(Message,long) 方法,即消息是由 MessageQueue 管理的
  • 消息又是怎么回到 handleMessage() 方法的?线程的切换是怎么回事?

Looper.loop() 是个死循环,会不断调用 MessageQueue.next() 获取 Message ,并调用 msg.target.dispatchMessage(msg) 回到了 Handler 来分发消息,以此来完成消息的回调

Thread.foo(){
  Looper.loop()
    //不断循环获取Message
   -> MessageQueue.next()
  //获取到之后,Message.target 就是发送该消息的 Handler,回到该Handler分发消息
   -> Message.target.dispatchMessage()
     -> Handler.handleMessage()
}
复制代码

Handler.handleMessage() 所在的线程最终由调用 Looper.loop() 的线程所决定

平时我们用的时候从异步线程发送消息到 Handler,这个 Handler 的 handleMessage() 方法是在主线程调用的,所以消息就从异步线程切换到了主线程。这就是线程的切换

1.webp.jpg1.webp.jpg


小结

  • Looper :负责关联线程以及消息的分发,在调用 Looper 线程下从 MessageQueue 获取 Message,分发给 Handler ;
  • MessageQueue :一个消息队列,负责消息的存储与管理,负责管理由 Handler 发送过来的 Message ;
  • Handler : 负责发送并处理消息,面向开发者,提供 API,并隐藏背后实现的细节。

Handler 发送的消息由 MessageQueue 存储管理,并由 Loopler 负责回调消息到 handleMessage()。

线程的转换由 Looper 完成,handleMessage() 所在线程由 Looper.loop() 调用者所在线程决定。

具体查看Handler 都没搞懂,拿什么去跳槽啊? - 掘金 (juejin.cn)

Handler 泄露问题

为什么 Handler 会引发泄露呢?

其主要原因是,我们错误的使用了 Handler ;这导致我们在关闭 Activity 的时,Handler 或者其他还在运行的类仍引用了 Activity 。这就导致 Activity 无法被回收,就会发生泄露。

有可到达 Activity 的引用链,可能会导致泄露的情况:

  • Thread 或者 Handler 为匿名内部类,持有了外部的 Activity
  • Thread 没了,但是其运行的 Message 还在发送。这会导致 Looper 也间接持有 Activity

GC root原理:通过对枚举GCroot对象做引用可达性分析,即从GC root对象开始,向下搜索,形成的路径称之为 引用链。如果一个对象到GC roots对象没有任何引用,没有形成引用链,那么该对象等待GC回收。

GC root对象是什么?

Java中可以作为GC Roots的对象

1、虚拟机栈(javaStack)(栈帧中的局部变量区,也叫做局部变量表)中引用的对象。

2、方法区中的类静态属性引用的对象。

3、方法区中常量引用的对象。

4、本地方法栈中JNI(Native方法)引用的对象。

解决 Handler 泄露

  1. 强应用 Activity 改为弱引用
  2. 及时切断两大 GC Root 的引用链关系:  Main Looper 到 Message;以及结束子线程。

1.webp.jpg

步骤1:Handler设为静态内部类,且对 Activity 弱引用

private static class SafeHandler extends Handler {
    private WeakReference<HandlerActivity> ref;
    public SafeHandler(HandlerActivity activity) {
        this.ref = new WeakReference(activity);
    }
    @Override
    public void handleMessage(final Message msg) {
        HandlerActivity activity = ref.get();
        if (activity != null) {
            activity.handleMessage(msg);
        }
    }
}
复制代码

步骤2:onDestroy() 的时候切断引用链关系

//1.若子线程任务尚未结束,及时中断
@Override
public void onDestroy() {
    ...
    thread.interrupt();
}
//子线程中创建了 Looper 并成为了 Looper 线程的话,须手动 quit
@Override
public void onDestroy() {
    ...
    handlerThread.quitSafely();
}
//主线程的 Looper 不可quit,退出App就挂掉了,所以要清除所有未处理的 message
@Override
public void onDestroy() {
    ...
    mainHandler.removeCallbacksAndMessages(null);
}
复制代码

小结

Handler 处理中持有 Activity 的,其生命周期应当与 Activity 一致,才是正确的用法

最正确用法应该是:

  1. 使用 Handler 机制,采用弱引用 + 静态内部类。保证错误延长了周期也能正确 GC
  2. Activity结束时候,清空 Message、终止 Thread 或退出 Looper。及时切除引用链

正确在子线程使用 Toast

由于 Toast 就是依赖于 Handler 完成的,所以在子线程中需要学习完整的 Handler 使用方式

//完整的需要有下面 1 2 两步
class LooperThread extends Thread {
    public Handler mHandler;
    public void run() {
        Looper.prepare();//1
        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };
        Looper.loop();//2
    }
}
复制代码
//得出下列代码
new Thread(new Runnable() {
  @Override
  public void run() {
    Looper.prepare();
    Toast.makeText(HandlerActivity.this, "不会崩溃啦!", Toast.LENGTH_SHORT).show();
    Looper.loop();
  }
}).start();
复制代码

Looper一直死循环,为啥主线程不会卡死

死循环是为了保证主线程能一直执行下去,但是事实上他并不是简单的无限循环下去,而是会休眠的。当没有消息的时候,Handler会开始休眠,直到有消息传递的时候就会唤醒它。大部分情况下,Handler都是在休眠中。详细分析可以看Android中为什么主线程不会因为Looper.loop()里的死循环卡死?

相关文章
|
数据采集 机器学习/深度学习 数据可视化
R语言从数据到决策:R语言在商业分析中的实践
【9月更文挑战第1天】R语言在商业分析中的应用广泛而深入,从数据收集、预处理、分析到预测模型构建和决策支持,R语言都提供了强大的工具和功能。通过学习和掌握R语言在商业分析中的实践应用,我们可以更好地利用数据驱动企业决策,提升企业的竞争力和盈利能力。未来,随着大数据和人工智能技术的不断发展,R语言在商业分析领域的应用将更加广泛和深入,为企业带来更多的机遇和挑战。
|
数据采集 存储 数据库
python爬虫知识
【8月更文挑战第27天】python爬虫知识
123 2
|
存储 缓存 算法
Java多线程与并发-原理
Java多线程与并发-原理
136 0
|
存储 Java API
Android 数据存储(二)-SP VS DataStore VS MMKV
一、SharedPreferences 不同于文件的存储方式,如果要保存的键值集合相对较小,则应使用SharedReferences API。SharedReferences对象指向一个包含键值对的文件,并提供简单的读写方法。 本文从SharedReferences开始逐步引入Preference、MMKV。
1735 0
Android 数据存储(二)-SP VS DataStore VS MMKV
|
存储 安全 Linux
机器物理内存用光后,到底会怎么样?
以下一切在Linux OS(3.10.0)的运行环境下讨论: 我们都知道,进程内存资源不足后,会OOM crash,进程退出。 然而,如果机器的物理内存资源被占用光后,在不考虑cgroup kill的情况下,会怎么样呢?其上的进程又会表现如何? 有人说,机器会宕机。 有人说,进程申请内存失败会在进程...
346 0
机器物理内存用光后,到底会怎么样?
pip 还是用阿里源吧 清华源有时候不是最新的 有bug
pip 还是用阿里源吧 清华源有时候不是最新的 有bug
415 0
pip 还是用阿里源吧 清华源有时候不是最新的 有bug
|
安全 机器人 新能源
备受关注的时事热门词汇,您了解“专精特新”吗?
备受关注的时事热门词汇,您了解“专精特新”吗?
316 0
备受关注的时事热门词汇,您了解“专精特新”吗?
|
安全 开发工具 数据安全/隐私保护
远程 Git 仓库(细节问题)| 学习笔记
快速学习 远程 Git 仓库(细节问题)
142 0
远程 Git 仓库(细节问题)| 学习笔记
|
机器学习/深度学习 人工智能 自然语言处理
AI:2020年6月22日北京智源大会演讲分享之10:40-11:10Daniel教授《 可微分的加权有限状态机及其机器学习应用》、11:10何晓冬教授《启动“智源-京东”任务导向多模态对话大赛》
AI:2020年6月22日北京智源大会演讲分享之10:40-11:10Daniel教授《 可微分的加权有限状态机及其机器学习应用》、11:10何晓冬教授《启动“智源-京东”任务导向多模态对话大赛》
AI:2020年6月22日北京智源大会演讲分享之10:40-11:10Daniel教授《 可微分的加权有限状态机及其机器学习应用》、11:10何晓冬教授《启动“智源-京东”任务导向多模态对话大赛》

热门文章

最新文章