Android 不允许在子线程中更新 UI ,你有想过这是为什么吗?

前言

Handler 在 android 程序开发中使用的非常频繁、我们知道 android 是不允许在子线程中更新 UI 的,这就需要借助 Handler 来实现

  • 那么你是否想过为什么一定要这个这样子做呢?
  • 而且 Handler 的内部消息处理机制究竟是什么样的呢?
  • 我们了解了之后还可以做点什么事?

带着这些疑问我系统的学习了一个讲解 Handler 原理;想来想去,就先从 Handler 的几个简单的使用方法开始吧,而且在总结过程中为了方便以后快捷的查阅使用,尽量说的精炼,不牵涉到源码的追踪和解析

为什么用 handler?

考虑到 java 多线程线程安全问题, android 规定只能在 UI 线程修改 Activity 中的 UI ;为了在其他线程中可以修改 UI ,所以引入 Handler ,从其他线程传消息到 UI 线程,然后 UI 线程接受到消息时更新线程

Handler 的简单使用

假设在 Activity 中处理一个耗时任务,需要更新 UI ,简单看看我们平时是怎么处理的

override fun onCreate(savedInstanceState: Bundle?) {
    
    
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main3)
    // 请求网络
    subThread.start()
}

override fun onDestroy() {
    
    
    subThread.interrupt()
    super.onDestroy()
}

private val handler by lazy(LazyThreadSafetyMode.NONE) {
    
     MyHandler() }
private val subThread by lazy(LazyThreadSafetyMode.NONE) {
    
     SubThread(handler) }

private class MyHandler : Handler() {
    
    
    override fun handleMessage(msg: Message) {
    
    
        super.handleMessage(msg)
        // 主线程处理逻辑,一般这里需要使用弱引用持有 Activity 实例,以免内存泄漏
    }
}

private class SubThread(val handler: Handler) : Thread() {
    
    
    override fun run() {
    
    
        super.run()
        // 耗时操作 比如做网络请求

        // 网络请求完毕,咱们就得哗哗哗通知 UI 刷新了,直接直接考虑 Handler 处理,其他方案暂时不做考虑
        // 第一种方法,一般这个 data 是请求结果解析的内容
        handler.obtainMessage(1,data).sendToTarget()
        // 第二种方法
        val message = Message.obtain() // 尽量使用 Message.obtain() 初始化
        message.what = 1
        message.obj = data // 一般这个 data 是请求结果解析的内容
        handler.sendMessage(message)
        // 第三种方法
        handler.post(object : Thread() {
    
    
            override fun run() {
    
    
                super.run()
                // 处理更新操作
            }
        })
    }
}

上述代码非常简单,因为网络请求是一个耗时任务,所以我们新开了一个线程,并在网络请求结束解析完毕后通过 Handler 来通知主线程去更新 UI,简单采用了 3 种方式,细心的小伙伴可能会发现,其实第一种和第二种方法是一样的

就是利用 Handler 来发送了一个携带了内容 Message 对象,值得一提的是:我们应该尽可能地使用 Message.obtain() 而不是 new Message() 进行 Message 的初始化,主要是 Message.obtain() 可以减少内存的申请;所有方法最终都会调用这个方法:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    
    
    MessageQueue queue = mQueue;
    if (queue == null) {
    
    
        RuntimeException e = new RuntimeException(
                this + " sendMessageAtTime() called with no mQueue");
        Log.w("Looper", e.getMessage(), e);
        return false;
    }
    return enqueueMessage(queue, msg, uptimeMillis);
}

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

handler 用法

Message 可携带的数据

//通常作标志位,作区分
message.what;int//携带简单数据
message.arg1;int)
message.arg2;int//携带object数据类型
message.obj;(object)

handler 可以分发 Message 对象和 Runnable 对象到主线程中, 每个 Handler 实例,都会绑定到创建他的线程中(一般是位于主线程),它有两个作用:

  • 安排消息或 Runnable 在某个主线程中某个地方执行;
  • 安排一个动作在不同的线程中执行

Handler 中分发消息的一些方法

post(Runnable)

postAtTime(Runnablelong)

postDelayed(Runnable long)

sendEmptyMessage(int)

sendMessage(Message)

sendMessageAtTime(Messagelong)

sendMessageDelayed(Messagelong)

以上 post 类方法允许你排列一个 Runnable 对象到主线程队列中

sendMessage 类方法, 允许你安排一个带数据的Message对象到队列中,等待更新

本文 全面介绍了 Handler 的由来和在 Android 中的简单使用 , 希望大家在开发时会明白 Handler 机制的使用;所以为了是大家能够更好的学习 Handler 相关的知识点

在这里特别提供一份 Android Framework 源码学习笔记, 里面包含了这些年学习 Android 开发所遇到的 Handle 、AMS、PMS 等相关难题及其解决方案;有需要这份 Android Framework 源码学习笔记 的朋友:可以 私信 发送 “笔记” 即可 免费获取; 希望大家阅读过后,能够 查漏补缺;早日精通 Android Framework

笔记内容展示如下:

详解 Handler 消息机制

  • Handler 的实现原理
  • 子线程中能不能直接 new 一个 Handler ,为什么主线程可以
  • Handler 导致的内存泄露原因及其解决方案
  • 一个线程可以有几个 Handler ,几个 Looper ,几个 MessageQueue 对象
  • Handler的post 与 sendMessage 的区别和应用场景

完整版 Android Framework 源码学习笔记获取方式:私信 发送 “笔记” 即可 免费获取

对于程序员来说,要学习的知识内容、技术有太多太多,要想不被环境淘汰就只有不断提升自己,从来都是我们去适应环境,而不是环境来适应我们

技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面

加油!各位 Android 开发者们

猜你喜欢

转载自blog.csdn.net/m0_70748845/article/details/126645292
今日推荐