绘制流程

在 Android 的 View 系统中,draw(canvas) 和 dispatchDraw(canvas) 是绘制流程中的两个关键方法:

1. draw(canvas) 方法的作用

draw(canvas) 是 View 类中的核心绘制方法,它的主要职责包括:

  1. 绘制背景 - 调用 drawBackground(canvas)
  1. 保存画布状态 - 调用 canvas.save()
  1. 绘制内容 - 调用 onDraw(canvas)
  1. 绘制子视图 - 调用 dispatchDraw(canvas)
  1. 绘制前景 - 调用 onDrawForeground(canvas)
  1. 恢复画布状态 - 调用 canvas.restore()

2. dispatchDraw(canvas) 的作用

dispatchDraw(canvas) 专门负责绘制子视图,它的调用链是:

===========================================

draw(canvas) 
  ↓
dispatchDraw(canvas)  ← 在这里被调用
  ↓
onDraw(canvas)  ← 子视图的绘制

===========================================

无限递归的问题示例

自定义View 的部分代码:

override fun dispatchDraw(canvas: Canvas) {// 绘制矩形// ...// 调用父类的 dispatchDraw 来绘制子视图super.dispatchDraw(canvas)// 缩略图回调post { generateThumbnail() }
}private fun generateThumbnail() {try {// 创建缩略图val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)val canvas = Canvas(bitmap)// 这里调用 draw(canvas) 会导致问题draw(canvas)// ← 这里会再次触发 dispatchDraw!val thumbnail = Bitmap.createScaledBitmap(bitmap, 120, 80, true)thumbnailCallback?.onThumbnailUpdated(thumbnail)} catch (e: Exception) {Log.e(TAG, "Error generating thumbnail", e)}
}

4. 无限递归的原因

当你调用 draw(canvas) 时:

  1.  draw(canvas) 被调用
  2. draw(canvas) 内部调用 dispatchDraw(canvas)
  3. dispatchDraw(canvas) 被调用
  4. dispatchDraw(canvas) 内部调用 generateThumbnail()
  5. generateThumbnail() 内部调用 draw(canvas)
  6. 回到步骤 1,形成无限循环

5. 解决方案

这就是为什么我们修改了 generateThumbnail() 方法,直接绘制矩形而不调用 draw(canvas)

private fun generateThumbnail() {// 直接绘制矩形,避免调用 draw()for (rect in rects) {paint.color = rect.colorval left = rect.cx - rect.width / 2val top = rect.cy - rect.height / 2val right = rect.cx + rect.width / 2val bottom = rect.cy + rect.height / 2canvas.drawRect(left, top, right, bottom, paint)}
}

总结

draw(canvas) 调用 dispatchDraw(canvas) 是 Android View 系统的标准绘制流程,这是为了确保:

  1. 父视图先绘制自己的内容
  1. 然后绘制所有子视图
  1. 最后绘制前景

当你在 dispatchDraw() 中调用 draw() 时,就会打破这个流程,导致无限递归。正确的做法是直接操作 Canvas 进行绘制,而不是调用 View 的 draw() 方法。

bitMap问题

    /*** 生成缩略图*/private fun generateThumbnail() {try {// 创建缩略图大小的 Bitmapval thumbnailWidth = 120val thumbnailHeight = 80val bitmap = Bitmap.createBitmap(thumbnailWidth, thumbnailHeight, Bitmap.Config.ARGB_8888)val canvas = Canvas(bitmap)//            // 设置白色背景
//            canvas.drawColor(Color.WHITE)// 计算缩放比例val scaleX = thumbnailWidth.toFloat() / widthval scaleY = thumbnailHeight.toFloat() / heightval scale = minOf(scaleX, scaleY)// 计算居中偏移val offsetX = (thumbnailWidth - width * scale) / 2val offsetY = (thumbnailHeight - height * scale) / 2// 应用变换canvas.save()canvas.translate(offsetX, offsetY)canvas.scale(scale, scale)// 应用当前视图的变换(平移和缩放)val cx = width / 2fval cy = height / 2fcanvas.translate(this.offsetX, this.offsetY)canvas.scale(scaleFactor, scaleFactor, cx, cy)// 直接绘制矩形,避免调用 draw()for (rect in rects) {paint.color = rect.colorval left = rect.cx - rect.width / 2val top = rect.cy - rect.height / 2val right = rect.cx + rect.width / 2val bottom = rect.cy + rect.height / 2canvas.drawRect(left, top, right, bottom, paint)}canvas.restore()thumbnailCallback?.onThumbnailUpdated(bitmap)} catch (e: Exception) {Log.e(TAG, "Error generating thumbnail", e)}}

 内存消耗的巨大差异

之前的实现(大 Bitmap):

  • 假设屏幕尺寸:1080×1920 像素
  • Bitmap 大小:1080 × 1920 × 4 bytes = 8.3MB
  • 每次生成缩略图都需要 8.3MB 内存
  • 频繁创建和销毁大 Bitmap

现在的实现(小 Bitmap):

  • 缩略图尺寸:120×80 像素
  • Bitmap 大小:120 × 80 × 4 bytes = 38.4KB
  • 内存使用量减少了 99.5%

问题总结

1. 无限递归 + 大 Bitmap 的双重打击

之前的实现存在两个致命问题:

  1. 无限递归:draw(canvas) → dispatchDraw(canvas) → generateThumbnail() → draw(canvas)
  1. 大 Bitmap 创建:每次递归都创建 8.3MB 的 Bitmap

这导致:

  • CPU 使用率 100%
  • 内存快速耗尽(每秒可能创建几十个 8.3MB 的 Bitmap)
  • 应用崩溃或重启

2. Bitmap 缩放的开销

之前的实现还需要额外的缩放操作:

kotlin

Apply to DeepLearnVie...

// 额外的缩放操作

val scaledBitmap = Bitmap.createScaledBitmap(bitmap, 120, 80, true)

这个操作本身就很耗 CPU 和内存。

3. 频繁的垃圾回收

大 Bitmap 的频繁创建和销毁会触发:

  • 频繁的垃圾回收
  • 内存碎片化
  • 系统卡顿

再进一步优化: 

Android View 绘制流程 优化 (Bitmap 复用+内容变化检测+防抖调度策略)-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/914347.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/914347.shtml
英文地址,请注明出处:http://en.pswp.cn/news/914347.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

算法学习笔记:18.拉斯维加斯算法 ——从原理到实战,涵盖 LeetCode 与考研 408 例题

在随机化算法领域,拉斯维加斯(Las Vegas)算法以其独特的设计思想占据重要地位。与蒙特卡洛(Monte Carlo)算法不同,拉斯维加斯算法总能给出正确的结果,但运行时间具有随机性 —— 在最坏情况下可…

26-计组-指令执行过程

一、指令周期1. 定义与组成定义:CPU取出并执行一条指令所需的全部时间,称为指令周期。子周期划分:取指周期(必选):从存储器取指令到指令寄存器(IR)。间址周期(可选&#…

【JMeter】数据驱动测试

文章目录创建数据文件加载数据文件根据数据文件请求接口、传递参数拓展含义:根据数据的数量、内容,自动的决定用例的数据和内容。数据驱动测试用例。步骤: 创建数据文件加载数据文件根据数据文件请求接口、传递参数 创建数据文件 Jmeter支…

Springboot实现一个接口加密

首先来看效果这个主要是为了防止篡改请求的。 我们这里采用的是一个AOP的拦截,在有需要这样的接口上添加了加密处理。 下面是一些功能防篡改HMAC-SHA256 参数签名密钥仅客户端 & 服务器持有防重放秒级时间戳 有效窗口校验默认允许 5 分钟防窃听AES/CBC/PKCS5Pa…

斯坦福 CS336 动手大语言模型 Assignment1 BPE Tokenizer TransformerLM

所有代码更新至 https://github.com/WangYuHang-cmd/CS336/tree/main/assignment1-basics 作业文件结构: CS336/assignment1-basics/ ├── tests/ # 测试文件目录 │ ├── adapters.py # 适配器测试 │ ├── conftest.py # pyt…

Spring Cloud Gateway 实战指南

关键词:微服务、API网关、Spring Cloud Gateway、路由转发、限流熔断 ✅ 文章摘要 随着互联网应用规模的不断扩大,传统的单体架构逐渐向微服务架构转型。在微服务架构中,API 网关作为系统的入口点,承担了诸如请求路由、负载均衡、…

PyTorch自动微分:从基础到实战

目录 1. 自动微分是什么? 1.1 计算图 1.2 requires_grad 属性 2. 标量和向量的梯度计算 2.1 标量梯度 2.2 向量梯度 3. 梯度上下文控制 3.1 禁用梯度计算 3.2 累计梯度 4. 梯度下降实战 4.1 求函数最小值 4.2 线性回归参数求解 5. 总结 在深度学习中&a…

Spring AI 项目实战(十六):Spring Boot + AI + 通义万相图像生成工具全栈项目实战(附完整源码)

系列文章 序号文章名称1Spring AI 项目实战(一):Spring AI 核心模块入门2Spring AI 项目实战(二):Spring Boot + AI + DeepSeek 深度实战(附完整源码)3Spring AI 项目实战(三):Spring Boot + AI + DeepSeek 打造智能客服系统(附完整源码)4

从零到一:企业如何组建安全团队

在这个"黑客满天飞,漏洞遍地跑"的时代,没有安全团队的企业就像裸奔的勇士——虽然很有勇气,但结局往往很悲惨。 📋 目录 为什么要组建安全团队安全团队的核心职能团队架构设计人员配置策略技术体系建设制度流程建立实施…

业务访问控制-ACL与包过滤

业务访问控制-ACL与包过滤 ACL的定义及应用场景ACL(Access Control List,访问控制列表)是用来实现数据包识别功能的;ACL可以应用于诸多场景: 包过滤功能:对数据包进行放通或过滤操作。NAT(Netwo…

穿梭时空的智慧向导:Deepoc具身智能如何赋予导览机器人“人情味”

穿梭时空的智慧向导:Deepoc具身智能如何赋予导览机器人“人情味”清晨,当第一缕阳光透过高大的彩绘玻璃窗,洒在博物馆光洁的地板上,一位特别的“馆员”已悄然“苏醒”。它没有制服,却有着清晰的指引;它无需…

PostgreSQL 查询库中所有表占用磁盘大小、表大小

SELECTn.nspname AS schema_name,c.relname AS table_name,-- 1️⃣ 总大小(表 toast 索引)pg_size_pretty(pg_total_relation_size(c.oid)) AS total_size,-- 2️⃣ 表不包含索引(含 TOAST)pg_size_pretty(pg_total_relation_s…

日记-生活随想

最近鼠鼠也是来到上海打拼(实习)了,那么秉持着来都来了的原则,鼠鼠也是去bw逛了逛,虽说没票只能在外场看看😭。可惜几乎没有多少我非常喜欢的ip,不由感慨现在的二次元圈已经变样了。虽说我知道内…

串口A和S的含义以及RT的含义

A async 异步S sync 同步RT 收发U A RT 异步U SA RT 同步/异步

spring cloud负载均衡分析之FeignBlockingLoadBalancerClient、BlockingLoadBalancerClient

本文主要分析被 FeignClient 注解的接口类请求过程中负载均衡逻辑&#xff0c;流程分析使用的依赖版本信息如下&#xff1a;<spring-boot.version>3.2.1</spring-boot.version><spring-cloud.version>2023.0.0</spring-cloud.version><com.alibaba.…

ref 和 reactive

文章目录ref 和 reactive一、差异二、能否替代的场景分析&#xff08;1&#xff09;基本类型数据&#xff08;2&#xff09;对象类型数据&#xff08;3&#xff09;数组类型数据&#xff08;4&#xff09; 需要整体替换的场景三、替代方案与兼容写法1. 用 reactive 模拟 ref2. …

BatchNorm 与 LayerNorm:原理、实现与应用对比

BatchNorm 与 LayerNorm&#xff1a;原理、实现与应用对比 Batch Normalization (批归一化) 和 Layer Normalization (层归一化) 是深度学习中两种核心的归一化技术&#xff0c;它们解决了神经网络训练中的内部协变量偏移问题&#xff0c;大幅提升了模型训练的稳定性和收敛速度…

OcsNG基于debian一键部署脚本

&#x1f914; 为什么有了GLPI还要部署OCS-NG&#xff1f; 核心问题&#xff1a;数据收集的风险 GLPI直接收集的问题&#xff1a; Agent直接向GLPI报告数据时&#xff0c;任何收集异常都会直接影响资产数据库网络问题、Agent故障可能导致重复资产、错误数据、资产丢失无法对收集…

001_Claude开发者指南介绍

Claude开发者指南介绍 目录 Claude简介Claude 4 模型开始使用核心功能支持资源 Claude简介 Claude 是由 Anthropic 构建的高性能、可信赖和智能的 AI 平台。Claude 具备出色的语言、推理、分析和编程能力&#xff0c;可以帮助您解决各种复杂任务。 想要与 Claude 聊天吗&a…

004_Claude功能特性与API使用

Claude功能特性与API使用 目录 API 基础使用核心功能特性高级功能开发工具平台支持 API 基础使用 快速开始 通过 Anthropic Console 获取 API 访问权限&#xff1a; 在 console.anthropic.com/account/keys 生成 API 密钥使用 Workbench 在浏览器中测试 API 认证方式 H…