Android第十一次面试补充篇
Livedata内存泄漏解决
1. 未正确绑定 LifecycleOwner
原因:
使用 observe()
时未传入正确的 LifecycleOwner
(如 Activity/Fragment),或误用 Application
等长生命周期对象,导致观察者无法自动解除绑定。
解决方案:
- 确保在 Activity/Fragment 中观察 LiveData,并传入其自身的
LifecycleOwner
。 - 对于 Fragment,优先使用
viewLifecycleOwner
而不是this
,避免因 Fragment 生命周期与 View 生命周期不一致导致泄漏。
// Fragment 中正确用法
liveData.observe(viewLifecycleOwner) { data ->// 更新 UI
}
2. 误用 observeForever()
原因:
observeForever()
不会自动移除观察者,需手动调用 removeObserver()
。若忘记移除,观察者会一直存活,导致关联的 Context/View 泄漏。
解决方案:
- 尽量使用
observe()
替代observeForever()
。 - 必须使用时,在适当生命周期(如
onDestroy()
)中手动移除观察者。
private val observer = Observer<Data> { data -> /* ... */ }override fun onStart() {super.onStart()liveData.observeForever(observer)
}override fun onStop() {super.onStop()liveData.removeObserver(observer) // 必须手动移除
}
3. 匿名内部类或外部类引用
原因:
通过匿名内部类或非静态内部类观察 LiveData,隐式持有外部类(如 Activity)的引用。若 LiveData 存活时间更长,会导致外部类无法回收。
解决方案:
- 使用 静态内部类 + WeakReference 包裹观察者,或通过
LifecycleOwner
自动管理。 - 在
onDestroy()
中主动解除绑定。
// 使用 WeakReference 避免强引用
class MyObserver(activity: Activity) : Observer<Data> {private val weakActivity = WeakReference(activity)override fun onChanged(data: Data?) {weakActivity.get()?.updateUI(data)}
}
4. 全局 LiveData 持有 Context 引用
原因:
单例或全局类(如 ViewModel、Repository)中的 LiveData 持有 Activity/Fragment 的 Context 引用,导致短生命周期对象无法释放。
解决方案:
- 避免在 LiveData 中直接暴露 Context 或 View。
- 使用
Transformations
转换数据,确保 LiveData 仅传递纯数据(如 String、Model 等)。
// ViewModel 中仅处理数据
class MyViewModel : ViewModel() {private val _data = MutableLiveData<String>()val data: LiveData<String> = _datafun loadData() {_data.value = "Hello"}
}
检测工具
- LeakCanary:自动检测内存泄漏并生成报告。
- Android Profiler:分析内存分配,观察 LiveData 引用链。
- Lint 检查:提示潜在的生命周期不匹配问题。
模拟场景提问:
场景1:面试官提问技术方案
面试官:
“你之前做过自定义View吗?有没有遇到过性能问题,比如卡顿或者过度渲染?”
候选人:
“做过几个简单的自定义View,比如一个进度条和一个带动画的图表。性能问题确实遇到过,比如第一次做的时候没注意,父布局和子View都设置了背景色,结果测试时发现GPU过度绘制飙红了,后来用开发者工具检查才发现是重复绘制背景的问题。当时把不必要的背景都删了,还用了clipRect
限制绘制区域,性能才好转。”
场景2:追问优化细节
面试官:
“你提到的clipRect
具体是怎么用的?能不能举个例子说说优化思路?”
候选人:
“比如我要做一个横向滚动的自定义View,里面有很多元素。如果直接全部绘制,滚动的时候即便元素在屏幕外也会被GPU处理,浪费资源。后来我在onDraw()
里加了canvas.clipRect
,根据当前滚动的位置只绘制可见区域的内容。代码大概是先save
画布,然后clipRect
设置裁剪区域,再画内容,最后restore
。这样滚动时帧率明显提高了。”
场景3:跨场景技术迁移
面试官:
“如果现在有一个复杂的折线图,绘制时有大量的重叠路径和渐变效果,你会怎么优化它的渲染性能?”
候选人:
“首先我会检查onDraw()
里有没有频繁创建Paint
或者Path
对象,如果有的话会提前在初始化时创建好,避免重复开销。另外,渐变效果可能会比较吃性能,如果是静态的折线图,可以考虑用setLayerType
缓存成位图。如果有动态部分,比如实时更新的数据点,就把它们和静态背景分开绘制,只刷新变化的区域。还可以试试用ConstraintLayout
减少外层容器的嵌套层次,减轻测量布局的压力。”
场景4:工具使用与调试
面试官:
“如果现在有一个页面突然掉帧严重,怀疑是自定义View的渲染问题,你怎么快速定位原因?”
候选人:
“我会先打开手机的‘调试GPU过度绘制’选项,观察页面是不是有大面积红色区域,确认是不是过度绘制的问题。如果是的话,先用Layout Inspector看层级有没有冗余背景,或者透明的View叠加太多。如果问题出在自定义View内部,就在Android Studio里开Profiler抓取CPU和GPU的使用情况,重点看onDraw()
的执行时间。如果是特定操作卡顿,比如滑动时掉帧,还可以用Systrace看看UI线程有没有被阻塞,或者GPU渲染管线哪里卡住了。”
场景5:开放思考
面试官:
“如果产品经理希望在一个列表项里同时实现圆角、阴影和透明度动画,但你觉得这样会导致过度渲染,会怎么沟通?”
候选人:
“我可能会先做一个原型,对比有无这些效果的性能数据,比如帧率和GPU负载,用实际数据说明问题。然后提替代方案,比如用图片预渲染静态阴影,或者用Lottie实现轻量级动画。如果是圆角导致的性能问题,可以建议外层容器统一裁剪圆角,而不是每个子View单独设置。关键是要明确告诉对方取舍——这些效果加在一起体验可能更‘精致’,但如果导致列表滑动卡顿,反而会影响核心操作。”
自定义 View 的 过度渲染(Overdraw)
通常表现为同一像素区域被多次绘制,导致 GPU 负载过高,影响性能(如卡顿、掉帧)。以下是常见原因和解决方案:
1. 减少不必要的背景绘制
-
问题:
若父布局和子 View 均设置了背景色,会导致重叠区域多次绘制。 -
解决方案:
-
移除冗余的背景设置(如默认背景)。
-
使用
android:background="@null"
或setBackground(null)
。 -
对于透明背景,尽量复用或通过
onDraw()
直接绘制。
-
<!-- 移除不必要的背景 -->
<LinearLayoutandroid:background="@null"> <!-- 或透明背景 --><TextViewandroid:background="@null" />
</LinearLayout>
2. 优化 onDraw()
方法
-
问题:
onDraw()
中执行复杂计算、频繁创建对象或重复绘制同一区域。 -
解决方案:
-
避免在
onDraw()
中创建对象:如Paint
、Path
,应在构造函数中初始化并复用。 -
限制绘制区域:使用
canvas.clipRect()
或canvas.clipPath()
裁剪绘制范围,避免绘制不可见区域。 -
合并绘制操作:将多个图形合并到同一 Path 或 Bitmap 中,减少绘制调用次数。
-
override fun onDraw(canvas: Canvas) {// 使用 clipRect 限制绘制区域(仅绘制可见部分)canvas.save()canvas.clipRect(scrollX, scrollY, width, height)// 绘制内容canvas.drawPath(mPath, mPaint)canvas.restore()
}
3. 使用硬件加速和缓存
-
问题:
复杂图形(如圆角、阴影)的实时计算会消耗 GPU 资源。 -
解决方案:
-
启用硬件加速:在 Manifest 或 View 级别开启硬件加速(默认开启,API 14+)。
-
缓存静态内容:通过
setLayerType(LAYER_TYPE_HARDWARE, null)
将复杂图形缓存为位图。 -
动态内容分情况处理:静态部分缓存,动态部分单独更新。
-
// 缓存复杂绘制内容(硬件缓存)
setLayerType(View.LAYER_TYPE_HARDWARE, null)// 仅在数据变化时更新
fun updateData(data: Data) {mData = datainvalidate() // 仅触发必要区域的重绘
}
4. 降低 View 层级复杂度
-
问题:
嵌套的 ViewGroup 或复杂布局会增加测量(Measure)和布局(Layout)的开销。 -
解决方案:
-
使用扁平化布局:优先使用
ConstraintLayout
替代多层嵌套的LinearLayout
/RelativeLayout
。 -
合并自定义 View 的子 View:将多个绘制逻辑合并到单一
onDraw()
中,避免嵌套子 View。 -
延迟加载:对非立即可见的部分使用
ViewStub
。
-
<!-- 使用 ConstraintLayout 减少嵌套 -->
<androidx.constraintlayout.widget.ConstraintLayout><TextView ... /><ImageView ... />
</androidx.constraintlayout.widget.ConstraintLayout>
5. 避免透明度和叠加效果
-
问题:
设置alpha
或setTranslationZ
会导致额外的离屏缓冲(Offscreen Buffer)绘制。 -
解决方案:
-
减少透明 View 的叠加使用。
-
使用
setHasOverlappingRendering(false)
告诉系统当前 View 无重叠内容,优化渲染。 -
优先通过
onDraw()
直接绘制透明度效果,而非设置 View 的全局alpha
。
-
// 声明 View 无重叠渲染(优化 GPU)
class MyView : View {init {setHasOverlappingRendering(false)}
}
6. 使用工具定位问题
-
GPU 过度绘制调试:
在开发者选项中打开 “调试 GPU 过度绘制”,颜色标识:-
无色(1 次绘制) → 理想状态
-
蓝色(2 次) → 可接受
-
绿色(3 次) → 需优化
-
红色(≥4 次) → 必须修复
-
-
性能分析工具:
-
Android Studio Profiler:分析 CPU/GPU 使用率和帧率。
-
Systrace/Perfetto:追踪渲染流水线,定位卡顿具体阶段。
-
Layout Inspector:检查 View 层级和属性。
-
Array、ArrayList和LinkedList是常用的数据结构
它们在性能和适用场景上有显著差异。以下是详细的对比和分析:
1. 性能对比
操作 | Array | ArrayList | LinkedList |
---|---|---|---|
随机访问(get) | O(1) | O(1) | O(n)(需遍历节点) |
尾部插入/删除 | O(1) | O(1)(均摊) | O(1) |
中间/头部插入/删除 | O(n)(需移动元素) | O(n)(需移动元素) | O(1)(已知位置时) |
内存占用 | 连续内存,无额外开销 | 连续内存,预留扩容空间 | 非连续,每个节点额外存储指针 |
扩容机制 | 固定长度 | 动态扩容(1.5倍) | 无需扩容 |
2. 核心区别
-
Array
-
特点:固定长度,内存连续,无额外开销。
-
优势:访问极快,内存紧凑。
-
劣势:无法动态扩容,插入/删除中间元素效率低。
-
-
ArrayList
-
特点:基于动态数组实现,自动扩容,支持泛型。
-
优势:访问快,尾部操作高效,API丰富。
-
劣势:中间插入/删除需移动元素,扩容时有复制开销。
-
-
LinkedList
-
特点:基于双向链表实现,无需连续内存。
-
优势:任意位置插入/删除快(已知节点时),无扩容开销。
-
劣势:随机访问慢,内存占用高(存储指针)。
-
3. 应用场景
Array
-
适用场景:
-
数据量固定且已知。
-
高频随机访问,对内存敏感(如底层算法、多维数据存储)。
-
例如:存储一周的日期、图像像素数据。
-
ArrayList
-
适用场景:
-
数据量动态变化,且以随机访问和尾部操作为主。
-
例如:分页查询结果、日志记录列表。
-
优化技巧:预分配容量(
ensureCapacity
)减少扩容次数。
-
LinkedList
-
适用场景:
-
频繁在任意位置(尤其是头部/中间)插入/删除。
-
需要实现队列、栈或双向队列(
Deque
)。 -
例如:任务调度系统、浏览器历史记录(支持前进/后退)。
-
4. 实际选择建议
-
优先选择ArrayList:
大多数场景下,ArrayList在随机访问和尾部操作上的性能更优,且内存局部性更好(缓存友好)。即使需要扩容,预分配容量可缓解性能问题。 -
慎用LinkedList:
仅在需要频繁中间插入/删除,或实现双端操作时使用。注意其随机访问性能差,且内存占用较高。 -
Array的特殊用途:
适用于对性能和内存有极致要求的场景,或与其他API交互时需要固定长度数组。
相关文章:
Android第十一次面试补充篇
Livedata内存泄漏解决 1. 未正确绑定 LifecycleOwner 原因: 使用 observe() 时未传入正确的 LifecycleOwner(如 Activity/Fragment),或误用 Application 等长生命周期对象,导致观察者无法自动解除绑定。 …...

【Zephyr 系列 3】多线程与调度机制:让你的 MCU 同时干多件事
好的,下面是Zephyr 系列第 3 篇:聚焦 多线程与调度机制的实践应用,继续面向你这样的 Ubuntu + 真板实战开发者,代码清晰、讲解通俗、结构规范,符合 CSDN 高质量博客标准。 🧠关键词:Zephyr、线程调度、k_thread、k_sleep、RTOS、BluePill 📌适合人群:想从裸机开发进…...

Kotlin-特殊类型
文章目录 数据类型枚举类型匿名类和伴生对象单例类伴生对象 数据类型 声明一个数据类非常简单: //在class前面添加data关键字表示为一个数据类 data class Student(var name: String, var age: Int)数据类声明后,编译器会根据主构造函数中声明的所有属性自动为其生成以下函数…...

nssctf第二题[SWPUCTF 2021 新生赛]简简单单的逻辑
这是题目,下载后得到一个python文件,打开 解读代码: for i in range(len(list)):key (list[i]>>4)((list[i] & 0xf)<<4)result str(hex(ord(flag[i])^key))[2:].zfill(2)list[i]>>4:从列表中取数字同时高4位向右位…...

《Discuz! X3.5开发从入门到生态共建》第3章 Discuz! X3.5 核心目录结构解析-优雅草卓伊凡
《Discuz! X3.5开发从入门到生态共建》第3章 Discuz! X3.5 核心目录结构解析-优雅草卓伊凡 3.1 系统核心目录结构 Discuz! X3.5采用模块化设计,主要目录结构如下: discuz_root/ ├─ api/ // API接口目录 ├─ config/ …...

【HarmonyOS 5】鸿蒙应用实现发票扫描、文档扫描输出PDF图片或者表格的功能
【HarmonyOS 5】鸿蒙应用实现发票扫描、文档扫描输出PDF图片或者表格的功能 一、前言 图(1-1) HarmonyOS 系统提供的核心场景化视觉服务,旨在帮助开发者快速实现移动端文档数字化功能。 其核心能力包括:扫描合同、票据、会议记录并保存为 PDF 分享。拍摄课堂 PPT、书籍章…...

Python_day43
DAY 43 复习日 作业: kaggle找到一个图像数据集,用cnn网络进行训练并且用grad-cam做可视化 进阶:并拆分成多个文件 关于 Dataset 从谷歌图片中抓取了 1000 多张猫和狗的图片。问题陈述是构建一个模型,该模型可以尽可能准确地在图像…...

STM32CubeDAC及DMA配置
STM32CubeDAC及DMA配置 一,问题1二,解决11,宏观思路CubeMX配置2,HAL_TIM_Base_Start(&htim6) 的作用1,作用1:使能TIM6的时钟并让它开始计数2,作用2:当 TIM6 溢出时,会…...
SQL快速入门【转自牛客网】
来源:牛客网 1、SQL 基础查询 在 SQL 中,SELECT 语句是最基本的查询语句,用于从数据库表中检索数据。通过 SELECT 语句,可以选择表中的所有列或特定列,并根据需要进行过滤和排序。 基本语法 SELECT 语句的基本语法如下: SELECT column1, column2, ... FROM table_na…...

行业案例 | OPPO借助Azure AI Speech国际服务实现音频文件智能转录
OPPO是全球领先的智能终端与移动互联网服务提供商,业务覆盖50余国,通过超40万销售网点和2500个服务中心与全球用户共享科技。作为软硬服一体化科技公司,OPPO以ColorOS为核心优化软件平台,为4.4亿月活用户打造智能操作系统…...

基于 OpenCV 和 DLib 实现面部特征调整(眼间距、鼻子、嘴巴)
摘 要 本文介绍如何利用Dlib面部特征点检测和OpenCV图像处理技术,通过Python实现面部特征的精准调整。我们将以改变眼间距为例,演示包括地标检测、三角剖分变形等关键技术,该方法可扩展至嘴唇、眉毛等面部特征的调整。 技术栈 Python 3.8 …...

spring-boot接入websocket教程以及常见问题解决
我们使用spring-boot接入websocket有三种方式:使用EnableWebSocket、EnableWebSocketMessageBroker以及ServerEndpoint,本文主要介绍使用ServerEndpoint方式的流程以及碰到的问题解决 接入方式 添加依赖 确保spring-boot-starter-websocket依赖 <d…...

迈向分布式智能:解析MCP到A2A的通信范式迁移
智能体与外部世界的桥梁之言: 在深入探讨智能体之间的协作机制之前,我们有必要先厘清一个更基础的问题:**单个智能体如何与外部世界建立连接?** 这就引出了我们此前介绍过的 **MCP(Model Context Protocol&…...

深度学习|pytorch基本运算-hadamard积、点积和矩阵乘法
【1】引言 pytorch对张量的基本运算和线性代数课堂的教学有一些区别,至少存在hadamard积、点积和矩阵乘法三种截然不同的计算方法。 【2】hadamard积 hadamard积是元素对位相乘,用“*”连接张量,代码: # 导入包 import torch …...

FFmpeg移植教程(linux平台)
目录 第三方源码编译三部曲关于 configure 的说明 FFmpeg 移植流程获取源码方法一:git 远程克隆方法二:官网下载压缩包解压 配置安装 第三方源码编译三部曲 Linux平台下有许多开源的第三方库和服务,这些开源代码一般都符合GNU-autotools编码…...

Mybatis:灵活掌控SQL艺术
在前面的文章中,小编分享了spring中相关的知识,但是没有分享到,如何去更高效操作数据库。 操作数据库传统的方法就是通过JDBC来进行操作。 这个传统方法使用上可谓是够麻烦的 1.首先创建一个数据源对象 2.设置该数据源的属性(…...

2025.05.28【Choropleth】群体进化学专用图:区域数据可视化
Load geospatial data Start by loading your geospatial data in R, and build a basic plot. Data from the package The cartography comes with a set of geospatial data included. Learn how to use it to build a choropleth map. 文章目录 Load geospatial dataData …...
Java设计模式详解:策略模式(Strategy Pattern)
在软件开发中,设计模式是解决常见问题的经典方法。策略模式(Strategy Pattern)作为一种行为型设计模式,能够将算法或行为的定义与使用分离,使得算法可以独立于客户端代码进行变化和扩展。本文将深入解析策略模式的核心…...

【春秋云镜】CVE-2022-26965 靶场writeup
知识点 网站的主题或者模块位置一般是可以上传文件的,不过一般为压缩包形式主题或者模块可以上github上找到和cms匹配的源码主题被解压后会放到加入到对应的文件夹中,而且还会自动执行对应的info.php文件(需要主题和cms配套才行)我这里取巧了࿰…...
爬虫的几种方式(使用什么技术来进行一个爬取数据)
在网页数据爬取中,确实存在多种数据呈现和获取形式,远不止静态HTML解析和简单JS渲染。理解这些形式对于应对不同的反爬机制至关重要: 主要数据获取形式与应对策略 纯静态HTML (基础形式) 特点: 数据直接嵌入在服务器返回的初始HT…...
XML 编码:结构化数据的基石
XML 编码:结构化数据的基石 引言 XML(可扩展标记语言)作为互联网上广泛使用的数据交换格式,已经成为结构化数据存储和传输的重要工具。本文旨在深入探讨XML编码的原理、应用场景以及编码规范,帮助读者更好地理解和运用XML。 XML编码概述 1. XML的起源 XML诞生于1998年…...
nt!CcGetVacbMiss函数分析之设置好nt!_VACB然后调用函数nt!SetVacb
第一部分:MmMapViewInSystemCache函数返回 Status MmMapViewInSystemCache (SharedCacheMap->Section, &Vacb->BaseAddress, &NormalOffset, …...

JSP、HTML和Tomcat
9x9上三角乘法表 乘法表的实现 <% page contentType"text/html;charsetUTF-8" language"java" %> <!DOCTYPE html> <html> <head><title>99 上三角乘法表</title><style>body {font-family: monospace;padding…...

(1)pytest简介和环境准备
1. pytest简介 pytest是python的一种单元测试框架,与python自带的unittest测试框架类似,但是比unittest框架使用起来更简洁,效率更高。根据pytest的官方网站介绍,它具有如下特点: 非常容易上手,入门简单&a…...
Git 入门学习教程
Git 入门学习教程 什么是 Git? Git 是一个分布式版本控制系统,由 Linus Torvalds 为 Linux 内核开发而创建。它可以帮助开发者: 跟踪代码变化协作开发项目回退到之前的版本创建分支进行实验性开发 安装 Git Windows 下载 Git for Windo…...

构建高性能风控指标系统
一、引言 在金融风控领域,指标是风险识别的核心依据。风控平台核心系统之一--规则引擎的运行依赖规则、变量和指标,一个高性能的指标系统非常重要,本文将深入探讨风控平台指标系统的全链路技术实现,涵盖从指标配置到查询优化的完…...
openfeignFeign 客户端禁用 SSL
要针对特定的 Feign 客户端禁用 SSL 验证,可以通过自定义配置类实现。以下是完整解决方案: 1. 创建自定义配置类(禁用 SSL 验证) import feign.Client; import feign.httpclient.ApacheHttpClient; import org.apache.http.conn…...
DeepSeek 赋能自动驾驶仿真测试:解锁高效精准新范式
目录 一、自动驾驶仿真测试概述1.1 自动驾驶发展现状1.2 自动驾驶仿真测试流程 二、DeepSeek 技术剖析2.1 DeepSeek 简介2.2 DeepSeek 核心技术原理 三、DeepSeek 在自动驾驶仿真测试中的应用原理3.1 与自动驾驶仿真测试流程的结合点3.2 如何提升仿真测试效果 四、DeepSeek 在自…...

晨控CK-UR12与西门子PLC配置Modbus TCP通讯连接操作手册
晨控CK-UR12与西门子PLC配置Modbus TCP通讯连接操作手册 晨控CK-UR12系列作为晨控智能工业级别RFID读写器,支持大部分工业协议如RS232、RS485、以太网。支持工业协议Modbus RTU、Modbus TCP、Profinet、EtherNet/lP、EtherCat以及自由协议TCP/IP等。 本期主题:围绕…...

实验一:PyTorch基本操作实验
import torch # PyTorch中初始化矩阵常见有以下几种方法 # 1. 直接使用固定值初始化 # M torch.tensor([[1.0, 2.0, 3.0]]) # 1x3矩阵 # 2. 随机初始化 # M torch.rand(1, 3) # 1x3矩阵,元素在0-1之间均匀分布 # M torch.randn(1, 3) # 1x3矩阵,元…...