记共享元素动画导致的内存泄露
最近在给项目的预览图片页增加共享元素动画的时候,发现了LeakCanary一直报内存泄露。
LeakCanary日志信息
┬───
│ GC Root: Thread object
│
├─ java.lang.Thread instance
│ Leaking: NO (the main thread always runs)
│ Thread name: 'main'
│ ↓ Thread.threadLocals
│ ~~~~~~~~~~~~
├─ java.lang.ThreadLocal$ThreadLocalMap instance
│ Leaking: UNKNOWN
│ Retaining 15.5 kB in 98 objects
│ ↓ ThreadLocal$ThreadLocalMap.table
│ ~~~~~
├─ java.lang.ThreadLocal$ThreadLocalMap$Entry[] array
│ Leaking: UNKNOWN
│ Retaining 15.5 kB in 97 objects
│ ↓ ThreadLocal$ThreadLocalMap$Entry[36]
│ ~~~~
├─ java.lang.ThreadLocal$ThreadLocalMap$Entry instance
│ Leaking: UNKNOWN
│ Retaining 28 B in 1 objects
│ ↓ ThreadLocal$ThreadLocalMap$Entry.value
│ ~~~~~
├─ android.util.ArrayMap instance
│ Leaking: UNKNOWN
│ Retaining 544 B in 21 objects
│ ↓ ArrayMap.mArray
│ ~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ Retaining 503 B in 19 objects
│ ↓ Object[3]
│ ~~~
├─ android.transition.Transition$AnimationInfo instance
│ Leaking: UNKNOWN
│ Retaining 141 B in 6 objects
│ ↓ Transition$AnimationInfo.transition
│ ~~~~~~~~~~
├─ android.transition.Fade instance
│ Leaking: UNKNOWN
│ Retaining 772 B in 21 objects
│ ↓ Transition.mParent
│ ~~~~~~~
├─ android.transition.TransitionSet instance
│ Leaking: UNKNOWN
│ Retaining 1.5 kB in 50 objects
│ ↓ Transition.mListeners
│ ~~~~~~~~~~
├─ java.util.ArrayList instance
│ Leaking: UNKNOWN
│ Retaining 116 B in 5 objects
│ ↓ ArrayList[1]
│ ~~~
├─ android.transition.TransitionManager$MultiListener$1 instance
│ Leaking: UNKNOWN
│ Retaining 36 B in 2 objects
│ Anonymous subclass of android.transition.TransitionListenerAdapter
│ ↓ TransitionManager$MultiListener$1.val$runningTransitions
│ ~~~~~~~~~~~~~~~~~~~~~~
├─ android.util.ArrayMap instance
│ Leaking: UNKNOWN
│ Retaining 541.5 kB in 8916 objects
│ ↓ ArrayMap.mArray
│ ~~~~~~
├─ java.lang.Object[] array
│ Leaking: UNKNOWN
│ Retaining 541.5 kB in 8914 objects
│ ↓ Object[8]
│ ~~~
├─ com.android.internal.policy.DecorView instance
│ Leaking: YES (View.mContext references a destroyed activity)
│ Retaining 136.4 kB in 2235 objects
│ View not part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mWindowAttachCount = 1
│ mContext instance of com.android.internal.policy.DecorContext, wrapping activity com.fengqun.whitepeachplanet.
│ activity.ImagePreviewActivity with mDestroyed = true
│ ↓ DecorView.mContentRoot
├─ android.widget.LinearLayout instance
│ Leaking: YES (DecorView↑ is leaking and View.mContext references a destroyed activity)
│ Retaining 3.0 kB in 36 objects
│ View is part of a window view hierarchy
│ View.mAttachInfo is null (view detached)
│ View.mWindowAttachCount = 1
│ mContext instance of com.fengqun.whitepeachplanet.activity.ImagePreviewActivity with mDestroyed = true
│ ↓ View.mContext
╰→ com.fengqun.whitepeachplanet.activity.ImagePreviewActivity instance
Leaking: YES (ObjectWatcher was watching this because com.fengqun.whitepeachplanet.activity.ImagePreviewActivit
received Activity#onDestroy() callback and Activity#mDestroyed is true)
Retaining 36.0 kB in 739 objects
key = cd70fcef-af19-457e-bb88-2350945ca1c4
watchDurationMillis = 5762
retainedDurationMillis = 753
mApplication instance of com.fengqun.whitepeachplanet.MyApplication
mBase instance of androidx.appcompat.view.ContextThemeWrapper
经过排查发现泄露的关键代码在这个 ActivityOptions.makeSceneTransitionAnimation 上。那么就从这里开始深入分析里面内容。
启动共享元素动画:
ActivityCompat.startActivity(activity,this,ActivityOptions.makeSceneTransitionAnimation(activity, *transitionImpl).toBundle()
)
这会创建包含共享元素信息的ActivityOptions对象
启动Activity:
@Override
public void startActivity(Intent intent, @Nullable Bundle options) {getAutofillClientController().onStartActivity(intent, mIntent);if (options != null) {startActivityForResult(intent, -1, options);} else {// Note we want to go through this call for compatibility with// applications that may have overridden the method.startActivityForResult(intent, -1);}
}
因为携带了Bundle,
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,@Nullable Bundle options) {if (mParent == null) {options = transferSpringboardActivityOptions(options);Instrumentation.ActivityResult ar =mInstrumentation.execStartActivity(this, mMainThread.getApplicationThread(), mToken, this,intent, requestCode, options);//...
}
public ActivityResult execStartActivity(Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {//...try {intent.migrateExtraStreamToClipData(who);intent.prepareToLeaveProcess(who);int result = ActivityTaskManager.getService().startActivity(whoThread,who.getOpPackageName(), who.getAttributionTag(), intent,intent.resolveTypeIfNeeded(who.getContentResolver()), token,target != null ? target.mEmbeddedID : null, requestCode, 0, null, options);notifyStartActivityResult(result, options);checkStartActivityResult(result, intent);} catch (RemoteException e) {throw new RuntimeException("Failure from system", e);}return null;
}
这里的ActivityTaskManager.getService() 实际返回的是 IActivityTaskManager 接口的 Binder 代理对象。实际上是ActivityTaskManagerService处理了startActivity()。通过在线源码阅读 可以得知他的调用应该发生在更底层的窗口。
这时我们在看看LeakCanary提供的信息。
↓ TransitionManager$MultiListener$1.val$runningTransitions
在TransitionManager找到了关键代码:
@Override
public boolean onPreDraw() {// Add to running list, handle end to remove itfinal ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =getRunningTransitions();ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);ArrayList<Transition> previousRunningTransitions = null;if (currentTransitions == null) {currentTransitions = new ArrayList<Transition>();runningTransitions.put(mSceneRoot, currentTransitions);} else if (currentTransitions.size() > 0) {previousRunningTransitions = new ArrayList<Transition>(currentTransitions);}return true;
}
这里的getRunningTransitions最终指向的是竟然是静态的成员变量:
private static ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>sRunningTransitions =new ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>();
@UnsupportedAppUsage
private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>();
这个静态的成员变量里面持有了ViewGroup。sPendingTransitions记录了正在进行的过渡动画的ViewGroup,而sRunningTransitions则通过ThreadLocal存储了当前运行的过渡动画。如果这些集合没有正确清理,可能会导致Activity被持续引用,而发生内存泄露
由于它们是私有的,考虑使用反射来访问,并将他们置空处理。 最终在onDestroy()方法中调用此方法
fun leakCanaryClean() {try {val pendingField = TransitionManager::class.java.getDeclaredField("sPendingTransitions")pendingField.isAccessible = true(pendingField.get(null) as? ArrayList<ViewGroup>)?.clear()val runningField = TransitionManager::class.java.getDeclaredField("sRunningTransitions")runningField.isAccessible = trueval threadLocal = runningField.get(null) as? ThreadLocal<*>threadLocal?.set(null)} catch (e: Exception) {LogUtils.e("清除预览图片反射异常: ${e.message}")}
}
至此因为系统持有ViewGroup导致的泄露问题就解决了。
当然这种内存泄露也不是递增的。通过AS 的Profiler可以看到,过段时间后。 gc还是能够回收掉ViewGroup的引用。 因为在Transition执行结束后,还是会remove掉的。当然系统也不会犯这种低级错误 🥲
最后通过简单的封装,就可以调用带动画预览效果了
ImageViewer.load(arrayList).selection(position).setShareView(viewMap.values.toList()).start()
相关文章:

记共享元素动画导致的内存泄露
最近在给项目的预览图片页增加共享元素动画的时候,发现了LeakCanary一直报内存泄露。 LeakCanary日志信息 ┬─── │ GC Root: Thread object │ ├─ java.lang.Thread instance │ Leaking: NO (the main thread always runs) │ Thread name: main │ …...

Flyweight(享元)设计模式 软考 享元 和 代理属于结构型设计模式
1.目的:运用共享技术有效地支持大量细粒度的对象 Flyweight(享元)设计模式 是一种结构型设计模式,它的核心目的是通过共享对象来减少内存消耗,特别是在需要大量相似对象的场景中。Flyweight 模式通过将对象的共享细节与…...
Win/Linux安装flash attention2
1.Win 安装Flash_attn (1)第一步:下载flash_attn-xxx.whl 文件 在 1)地址1:HuggingFace 官网 Flash-attn页面 2)地址2:Github 地址 下载对应cuda、torch、python版本的whl文件; …...
【原创】ubuntu22.04下载编译AOSP 15
安装依赖的库,顺便把vim 也安装一下 sudo apt-get install vim sudo apt-get install git gnupg flex bison build-essential zip curl zlib1g-dev libc6-dev-i386 x11proto-core-dev libx11-dev lib32z1-dev libgl1-mesa-dev libxml2-utils xsltproc unzip font…...

服务器网络配置 netplan一个网口配置两个ip(双ip、辅助ip、别名IP别名)
文章目录 问答 问 # This is the network config written by subiquity network:ethernets:enp125s0f0:dhcp4: noaddresses: [192.168.90.180/24]gateway4: 192.168.90.1nameservers:addresses:- 172.0.0.207- 172.0.0.208enp125s0f1:dhcp4: trueenp125s0f2:dhcp4: trueenp125…...
响应面法(Response Surface Methodology ,RSM)
响应面法是一种结合统计学和数学建模的实验优化技术,通过有限的实验数据,建立输入变量与输出响应之间的数学模型,找到最优操作条件。 1.RSM定义 RSM通过设计实验、拟合数学模型(如多项式方程)和分析响应曲面ÿ…...
针对面试-java集合篇
1.什么是数组 数组(Array)是一种用连续的内存空间存储相同数据类型数据的线性数据结构。 2.数组下标为什么从0开始 寻址公式是:baseAddressi*dataTyeSize,计算下标的内存地址效率较高 3.查找的时间复杂度 随机(通过下标)查询的时间复杂度是O(1) 查找元素(未知…...

Spring Boot 拦截器:解锁5大实用场景
一、Spring Boot中拦截器是什么 在Spring Boot中,拦截器(Interceptor)是一种基于AOP(面向切面编程)思想的组件,用于在请求处理前后插入自定义逻辑,实现权限校验、日志记录、性能监控等非业务功能…...
展锐 Android 15 锁定某个App版本的实现
Android 15 系统锁定Antutu版本的实现方法 在Android系统开发中,有时需要锁定特定应用的版本以确保系统稳定性或测试一致性。本文将介绍如何通过修改Android源码来锁定Antutu跑分软件的版本。 修改概述 这次修改主要涉及以下几个方面: 禁用产品复制文件的检查添加指定版本…...

有两个Python脚本都在虚拟环境下运行,怎么打包成一个系统服务,按照顺序启动?
环境: SEMCP searx.webapp python 问题描述: 有两个python脚本都在虚拟环境下运行,怎么打包成一个系统服务,按照顺序启动? 解决方案: 将这两个 Python 脚本打包成有启动顺序的系统服务,最…...
【Linux cmd】查找进程信息
1、包含 "Test" 关键字的进程 ps -ef | grep Test 显示系统中所有进程的详细信息,包括用户 ID(UID)、进程 ID(PID)、父进程 ID(PPID)、启动时间(STIME)、终端…...
与网格共舞 - 服务网格的运维与问题排查 (Istio 实例)
与网格共舞 - 服务网格的运维与问题排查 (Istio 实例) 在领略了服务网格(以 Istio 为例)在流量管理、可观测性和安全方面提供的强大能力后,我们自然会思考:如何将这个“神器”请进我们的生产环境,并让它稳定、可靠地运行?这需要我们关注运维层面的实践。 部署与升级:网…...

Python 脚本执行命令的深度探索:方法、示例与最佳实践
在现代软件开发过程中,Python 脚本常常需要与其他工具和命令进行交互,以实现自动化任务、跨工具数据处理等功能。Python 提供了多种方式来执行外部命令,并获取其输出,重定向到文件,而不是直接在终端中显示。这种能力使…...

PotPlayer 4K 本地万能影音播放器
今日分享一款来自吾爱论坛大佬分享的啥都能播的的本地播放器,不管是不管是普通视频、4K超清、蓝光3D,还是冷门格式,它基本都能搞定。而且运行流畅不卡顿,电脑配置低也能靠硬件加速,让你根本停不下来。 自带解码器&…...

2025年电工杯A题第一版本Q1-Q4详细思路求解+代码运行
A题 光伏电站发电功率日前预测问题 问题背景 光伏发电是通过半导体材料的光电效应,将太阳能直接转化为电能的技术。光伏电站是由众多光伏发电单元组成的规模化发电设施。 光伏电站的发电功率主要由光伏板表面接收到的太阳辐射总量决定,不同季节太阳光…...

基于阿里云DashScope API构建智能对话指南
背景 公司想对接AI智能体,用于客服系统,经过调研和实施,觉得DashScope 符合需求。 阿里云推出的DashScope灵积模型服务为开发者提供了便捷高效的大模型接入方案。本文将详细介绍如何基于DashScope API构建一个功能完善的智能对话系统&#x…...
HOW - 基于组件库组件改造成自定义组件基本规范
文章目录 Select 选择器改造1. 明确组件目标2. 定义组件 API3. 合理使用默认值4. 支持类型安全的 options 传递5. 支持 ForwardRef(可选)6. 封装样式(可选)7. 使用示例 ...props 位置推荐顺序:最后原因:简要…...

九州未来十三载:开源赋能 智启未来
2012年,九州未来以“开源赋能云边变革”为使命,开启中国开放云边基础架构服务的探索之路。十三载坚守深耕,我们始终以开源为翼,以算力为基,在科技浪潮中砥砺前行,见证并推动着AI时代的算力变革。 坚守初心丨…...

2025年AI搜索引擎发展洞察:技术革新与市场变革
引言:AI搜索的崛起与市场格局重塑 2024-2025年,AI搜索市场迎来了前所未有的变革期。随着DeepSeek-R1等先进大语言模型的推出,传统搜索引擎、AI原生搜索平台以及各类内容平台纷纷加速智能化转型,推动搜索技术从基础信息检索向深度…...

dify调用Streamable HTTP MCP应用
一、概述 上一篇文章,介绍了使用python开发Streamable HTTP MCP应用,链接:https://www.cnblogs.com/xiao987334176/p/18872195 接下来介绍dify如何调用MCP 二、插件 安装插件 需要安装2个插件,分别是:Agent 策略(支持 …...

HCIP实验五
一、实验拓扑图: 二、实验需求分析: 1. PreVal策略:要求确保R4通过R2到达192.168.10.0/24 ,需在R4上针对去往该网段路由配置PreVal策略,为经R2的路径赋予更高优先值,影响本地路由表选路。 2. AS Path策略…...
java将图片转Base64字符串存储mysql数据库
1、mysql数据库的表里新增一个字段image_data,使用TEXT或LONGTEXT类型: CREATE TABLE IMAGES( id INT AUTO_INCREMENT PRIMARY KEY, image_name VARCHAR(255), image_data LONGTEXT, upload_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); 2、Java核心…...
题目 3330: 蓝桥杯2025年第十六届省赛真题-01 串
题目 3330: 蓝桥杯2025年第十六届省赛真题-01 串 时间限制: 2s 内存限制: 192MB 提交: 310 解决: 24 题目描述 给定一个由 0, 1, 2, 3 的二进制表示拼接而成的长度无限的 01 串。 其前若干位形如 011011100101110111 。 请求出这个串的前 x 位里有多少个 1 。 输入格…...
初识 Flask 框架
目录 1. Flask 框架概述 1.1 安装 Flask 1.2 创建你的第一个 Flask 应用 1.3 运行 Flask 应用 2. Flask 路由与视图函数 2.1 动态路由 2.2 支持多种 HTTP 请求方法 2.3 使用 Jinja2 模版渲染 HTML 2.5 模版继承与块 3. Flask 表单处理与用户输入 3.1 安装 Flask-WTF …...
MYSQL故障排查和环境优化
一、MySQL故障排查 1. 单实例常见故障 (1)连接失败类问题 ERROR 2002 (HY000): Cant connect to MySQL server 原因:MySQL未启动或端口被防火墙拦截。 解决:启动MySQL服务(systemctl start mysqld)或开放…...

vivado fpga程序固化
一般下载到fpga上的程序在掉电之后就会丢失,如果想要掉电之后程序不丢失,就需要将比特流文件固化到板载的flash上。 以下以我的7a100t开发板为例,介绍程序固化的流程 点击OK就可以下载了。 一个奇怪的问题 有一次我的一个工程固化之后&…...

OpenCV CUDA模块图像特征检测与描述------图像中快速检测特征点类cv::cuda::FastFeatureDetector
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 cv::cuda::FastFeatureDetector 是 OpenCV 的 CUDA 加速模块中的一部分,用于在图像中快速检测特征点。FAST(Features fro…...

SpringMVC(结合源码浅析工作流程)
SpringMVC 概念 Spring MVC 是基于前端控制器(Front Controller)设计模式的 Web 框架,在 Web 应用中指一个统一的入口,用来接收所有客户端请求,并统一进行分发、处理。在 SpringMVC 中,前端控制器就是 Di…...

学习STC51单片机13(芯片为STC89C52RC)
我去,兄弟们我们今天来学习一个牛逼 的硬件,它叫超声波测距模块HC—SR04 硬件:HC—SR04 哎,想当初最想要玩的就是这个模块,科技感十足,那现在就让我们玩玩吧 超声波测距传感器 原理就是说需要给Trig 10u…...

Claude 4 系列 Opus 4 与 Sonnet 4正式发布:Claude 4新特性都有哪些?
随着 Claude 4 系列(Opus 4 与 Sonnet 4)的正式发布,Anthropic 把自家大模型从“会聊天”推进到“能当自主代理”──不仅推理更深、上下文更长,还内置代码执行、多模态理解、工具调用等一揽子全新能力;同时࿰…...