当前位置: 首页 > news >正文

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 开发者们

相关文章:

  • Numpy数组中的运算与拼接,看这篇就够了
  • Docker网络模型
  • U9二次开发之补丁制作
  • 数字孪生与元宇宙相比有何区别?
  • java计算机毕业设计计算机散件报价系统源码+数据库+系统+lw文档+mybatis+运行部署
  • Qt Quick/QML入门到精通_专栏demo对应文章目录(目前27个demo/长期更新)
  • Java并发 | 17.[锁机制] 重量级锁(Monitor+自旋锁)
  • 【我拥有的书】
  • MSP432P 汇编,C语言点灯
  • Tomcat部署及优化
  • Kubernetes中Pod容器的使用
  • BERT模型解析
  • Springboot整合dubbozookeeper为注册中心
  • Spark的部署与使用
  • 目标检测——关键点检测学习记录(二):人体骨骼点检测——自顶向下
  • 2018一半小结一波
  • 30秒的PHP代码片段(1)数组 - Array
  • Babel配置的不完全指南
  • CoolViewPager:即刻刷新,自定义边缘效果颜色,双向自动循环,内置垂直切换效果,想要的都在这里...
  • crontab执行失败的多种原因
  • express如何解决request entity too large问题
  • java 多线程基础, 我觉得还是有必要看看的
  • Puppeteer:浏览器控制器
  • react-core-image-upload 一款轻量级图片上传裁剪插件
  • SpringCloud集成分布式事务LCN (一)
  • 高程读书笔记 第六章 面向对象程序设计
  • 聚类分析——Kmeans
  • 深入浅出webpack学习(1)--核心概念
  • 什么是Javascript函数节流?
  • 事件委托的小应用
  • 我这样减少了26.5M Java内存!
  • 系统认识JavaScript正则表达式
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 运行时添加log4j2的appender
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 不要一棍子打翻所有黑盒模型,其实可以让它们发挥作用 ...
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • $.extend({},旧的,新的);合并对象,后面的覆盖前面的
  • (1)Nginx简介和安装教程
  • (1)虚拟机的安装与使用,linux系统安装
  • (a /b)*c的值
  • (LeetCode C++)盛最多水的容器
  • (附源码)计算机毕业设计高校学生选课系统
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理第3章 信息系统治理(一)
  • (详细版)Vary: Scaling up the Vision Vocabulary for Large Vision-Language Models
  • (一)Neo4j下载安装以及初次使用
  • (轉貼) 蒼井そら挑戰筋肉擂台 (Misc)
  • .NET Compact Framework 3.5 支持 WCF 的子集
  • .NET Core 中插件式开发实现
  • .NET Core中Emit的使用
  • .NET Framework .NET Core与 .NET 的区别
  • .net 发送邮件
  • .net 受管制代码
  • .Net 中Partitioner static与dynamic的性能对比
  • .NET 中让 Task 支持带超时的异步等待