ASM 字节码插桩:监控大图加载

Android Bitmap内存优化实战:从监控、分析到治理的完整闭环 在移动应用开发中,内存管理是保障应用流畅与稳定的核心技术。其核心原理在于高效管理应用运行时对有限系统内存资源的分配与回收,以避免内存泄漏和溢出(OOM)。这项技术的价值直接决定了应用的性能表现和用户体验,尤其在图片密集型的应用场景中,对内存的精细控制至关重要。图片作为移动应用中最常见的内存消耗大户,其加载、显示与缓存策略是内存优化的关键战场。通过编译时插桩(AOP)等技术,可以实现对图片创建行为的无侵入、全覆盖监控,并结合动态阈值与设备分级策略进行智能分析。最终,这些实践能自然收敛到对Android Bit 阅读详情

作者:业志陈

加载图片是一个很常规的操作,同时也是一个“成本”较高的行为,因为加载一张图片可能需要先后历经 网络请求、I/O 读写、内存占用 等多个过程。我们一般是通过 Coil、Glide 等开源库来加载图片,完全无需关心其加载过程,而其中可能就隐藏着一个不是很合理的情况:加载的图片属于大图,超出了展示所需

加载展示所需的图片会造成不必要的性能浪费,同时也可能会引发 OOM,因此进行应用性能优化的一个点就是检测应用全局的图片加载情况,本文就来介绍如何通过字节码插桩的方式来实现全局大图检测

首先,什么类型的图片属于大图呢?我觉得可以从两个方面来进行认定:

  • 图片的尺寸大于 ImageView 本身的尺寸。例如,ImageView 的宽高只有 100 dp,但图片却有 200 dp
  • 图片的大小超过一定阈值。例如,我们可以规定单张图片最多不能超出 1 MB,大于该值的图片就认为是大图

我们项目中使用的 ImageView 的类型又可以分为两种:

  • 系统内置的android.widget.ImageView。一般是在 XML 文件中通过 <ImageView/> 标签来进行使用
  • 开发者自定义的 ImageView 子类。一般也是在 XML 文件使用

因此,基本的实现思路就是:通过定义一个统一的 ImageView 供项目全局使用,用于替代系统内置的 ImageView 和各个自定义子类的直接父类,当 setImageDrawable 和 setImageBitmap 两个方法被调用时,就对 Drawable 的尺寸和大小进行检测,当检测到属于大图时就按照实际的业务情况进行数据上报

open class MonitorImageView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : android.widget.ImageView(context, attrs, defStyleAttr), MessageQueue.IdleHandler {

    companion object {

        private const val MAX_ALARM_IMAGE_SIZE = 1024

    }

    override fun setImageDrawable(drawable: Drawable?) {
        super.setImageDrawable(drawable)
        monitor()
    }

    override fun setImageBitmap(bm: Bitmap?) {
        super.setImageBitmap(bm)
        monitor()
    }

    private fun monitor() {
        Looper.myQueue().removeIdleHandler(this)
        Looper.myQueue().addIdleHandler(this)
    }

    override fun queueIdle(): Boolean {
        checkDrawable()
        return false
    }

    private fun checkDrawable() {
        val mDrawable = drawable ?: return
        val drawableWidth = mDrawable.intrinsicWidth
        val drawableHeight = mDrawable.intrinsicHeight
        val viewWidth = measuredWidth
        val viewHeight = measuredHeight
        val imageSize = calculateImageSize(mDrawable)
        if (imageSize > MAX_ALARM_IMAGE_SIZE) {
            log(log = "图片大小超标 -> $imageSize")
        }
        if (drawableWidth > viewWidth || drawableHeight > viewHeight) {
            log(log = "图片尺寸超标 -> drawable:$drawableWidth x $drawableHeight  view:$viewWidth x $viewHeight")
        }
    }

    private fun calculateImageSize(drawable: Drawable): Int {
        return when (drawable) {
            is BitmapDrawable -> {
                drawable.bitmap.byteCount
            }
            else -> {
                0
            }
        }
    }

    private fun log(log: String) {
        Log.e(javaClass.simpleName, log)
    }

}

当然,我们也不太可能采取硬编码的方式来直接修改项目中的原有逻辑,成本太高,不灵活,而且也无法照顾到外部依赖。此时通过字节码插桩的方式来实现就成了比较经济和高效的方案,可以做到多项目复用

对于开发者自定义的 ImageView 子类,我们只需要在 Transform 阶段,当检查到当前 Class 直接继承于系统的 ImageView,就将其改为继承于 MonitorImageView 即可。稍微麻烦一点的是在 XML 中声明的 <ImageView/> 标签

我们知道,在布局文件中声明的各个控件,在使用时都对应一个个具体的 View 实例对象,而想要将静态的 XML 声明转换为动态的实例对象,就需要通过解析 XML 文件并根据类路径来反射出实例对象了,这一部分逻辑就隐藏在 LayoutInflater 中,LayoutInflater 会根据我们传入的 layoutResID 来进行解析

另一方面,现如今我们在新建 Activity 时,一般都不会直接继承于系统内置的 android.app.Activity,而是使用 androidx.appcompat.app.AppCompatActivity,AppCompatActivity 提供了更多的兼容性保障,当中就包含了自定义实现的 LayoutInflater

AppCompatActivity 通过 AppCompatViewInflater 来解析 XML 文件,当判断到我们声明的是系统控件时(例如 TextView、ImageView、Button 等),就会使用对应的 AppCompatXXX 来生成相应的实例对象,ImageView 就对应 AppCompatImageView

所以说,大多数情况下我们使用的 ImageView 实例对应的其实都是 androidx.appcompat.widget.AppCompatImageView,而非 android.widget.ImageView。这就为我们提供了一个 hook 点:只要我们能够将 AppCompatImageView 的父类修改为我们自定义的 MonitorImageView,就可以来为应用全局实现一个统一的大图检测功能了

有了上述思路后,相应的插桩代码也就很简单了

class LegalBitmapTransform(private val config: LegalBitmapConfig) : BaseTransform() {

    private companion object {

        private const val ImageViewClass = "android/widget/ImageView"

        private const val AppCompatImageViewClass = "androidx/appcompat/widget/AppCompatImageView"

    }

    override fun modifyClass(byteArray: ByteArray): ByteArray {
        val classReader = ClassReader(byteArray)
        val className = classReader.className
        val superName = classReader.superName
        Log.log("className: $className superName: $superName")
        return if ((superName == ImageViewClass && className != config.formatMonitorImageViewClass) || className == AppCompatImageViewClass) {
            val classWriter = ClassWriter(classReader, ClassWriter.COMPUTE_MAXS)
            val classVisitor = object : ClassVisitor(Opcodes.ASM6, classWriter) {
                override fun visit(
                    version: Int,
                    access: Int,
                    name: String?,
                    signature: String?,
                    superName: String?,
                    interfaces: Array<out String>?
                ) {
                    super.visit(
                        version,
                        access,
                        name,
                        signature,
                        config.formatMonitorImageViewClass,
                        interfaces
                    )
                }
            }
            classReader.accept(classVisitor, ClassReader.EXPAND_FRAMES)
            classWriter.toByteArray()
        } else {
            byteArray
        }
    }

}

最后也给出完整的源码:ASM_Transform

Android Bitmap内存优化实战:从监控到治理的完整闭环方案 在移动应用开发中,内存管理是保障应用性能与稳定性的核心技术。Android系统采用Java与Native混合内存模型,其中Bitmap的像素数据自Android 8.0起主要存储在Native堆,这使得其内存分配与回收机制更为复杂,也增加了监控与优化的难度。理解Bitmap的内存原理,关键在于掌握其内存占用计算方式(宽×高×每像素字节数)以及Native堆与Java堆的关联。有效的内存优化能显著降低应用发生OOM(Out Of Memory)的风险,提升在低端设备上的流畅度与用户体验。其应用场景广泛覆盖了图 阅读详情

相关推荐

LargeImageMonitor:一个使用ASM进行字节码插桩的大图监控框架

LargeImageMonitor 背景 在平时开发中,我们经常会出现OOM和APK体积过大的情况,其中有很大的原因在于图片过大。当图片占用内存过大,内存不足时就会导致OOM。而图片的文件太大,会导致在打包成APK后,APK的体积增加。关于局部加载的图片,UI给我们图时,我们或许会下意识看一眼文件大小,如果文件太大我们可能会跟UI小姐姐进行沟通。对于从网络加载图片,很多时候客户端拿到的只是一个URL,我们并不知道要加载的图片大小,这时候就有可能造成图片过大的情况。 介绍 LargeImageMonitor是一个使用ASM进行字节码插桩的大图监控框架,可以对我们要加载的图片进行监控,如果出现图片超过阈值的情况会进行报警。 功能与特点 1.支持Glide,Fresco,Picasso,Universal Image Loader。 2.可以对图片的文件大小设置阈值。 3.可以对图片所占用的

【每日学点鸿蒙知识】输入框光标显示问题、web组件回调async问题、图片加载流程监控、背景图片大小不生效问题、alert无效

参考:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-develop-apply-immersive-effects-V5#section162965215413。系统的Image(’’) 组件有整个加载流程的监听api吗?比如:网络加载耗时、url转换(比如域名转换、裁切参数拼接等)、是否来源于磁盘、图片格式、解码耗时等等。专门为OpenHarmony打造的一款图像加载缓存库,致力于更高效、更轻便、更简单。

轻口味的专栏 765

Kylin系统离线环境下的dpkg依赖包高效部署指南

本文详细介绍了在Kylin系统离线环境下高效部署dpkg依赖包的完整方案,包括预下载依赖包、构建本地APT仓库以及高级故障排查技巧。通过实战经验和优化建议,帮助用户解决依赖地狱问题,提升部署效率,特别适用于银河麒麟V10等内网环境。

weixin_29051245的博客 388

Android大图监测的这三种实现方式,你最喜欢哪种?

Android中,图片占用的内存主要由其宽、高和每个像素的位数决定。中添加一次拦截器,即可在每个图片请求中进行通用的大图监测处理,而不用在每个请求的响应回调中添加监测代码。一般情况下,大图的定义是指超过一定阈值的图片。主动监测通过周期性地扫描内存中的图片资源,识别大图,进行处理。被动监测的目的是,让图在加载的过程中,自动获取到加载图片的大小。中的一种强大的机制,可以在请求发起和响应返回的过程中进行拦截、修改和监测。主动监测只要获取到内存中的图片资源,通过扫描判断是否超过设置的阈值即可。

Gaga246的博客 1173

机器学习发展历史及现状(重要的期刊,会议,大牛总结)

1 引言 机器学习是指通过计算机学习数据中的内在规律性信息,获得新的经验和知识,以提高计算机的智能性,使计算机能够像人那样去决策。随着各行业对数据的需求量增多,对处理和分析数据的效率要求变高,一系列机器学习算法应运而生。机器学习算法主要是指通过数学及统计方法求解最优化问题的步骤和过程。针对不同的数据和不同模型需求,选择和使用适当的的机器学习算法可以更高效地解决一些实际问题。本文将从机器学习的起源...

Oscar2018的博客 5万+

通信系统原理[郭宇春]——模拟调制系统——课后习题答案

通信系统原理[郭宇春]——第三章课后习题答案,出错望指正!

尧呱呱的博客 3844

Android 大图检测插件的落地

作者:layz4android在实际的项目开发中,引入图片的方式基本可以分为两种:本地图片和云端图片,对于云端图片来说,可以动态地配置图片的大小,如果服务端的伙伴下发的图片很大导致程序异常,那么可以随时修改端上无法发版修复。但是因为本地引入的图片,e.g. 背景图,位图 过大导致程序出现异常,则必须要紧急发版修复线上用户问题。

1 860

【DMA】认识 DMA 及其工作流程(与传统CPU存取方式的区别)

【DMA】认识 DMA 及其工作流程(与传统CPU存取方式的区别)

challenglistic的博客 5万+

Android大图监测系统的三种实现方式

在Android应用中,大图的加载和显示可能导致内存占用过高,进而引发OOM(Out Of Memory)异常,影响应用的稳定性和用户体验,为了更好地管理大图资源,我们需要建立起一套可靠的大图监测系统,文中有详细的代码示例供大家参考,需要的朋友可以参考下。通过这种方式,你只需要在OkHttp中添加一次拦截器,即可在每个图片请求中进行通用的大图监测处理,而不用在每个请求的响应回调中添加监测代码。如果你使用的是第三方图片加载库Glide,最简单的直接的是在图片加载的成功的时机进行监测。

chinaherolts2008的博客 165

国产平替NeuroSky?实测金牛座TGAM脑电模块,从Arduino到Python的完整数据采集指南

本文详细介绍了国产金牛座TGAM脑电模块的完整使用流程,包括硬件连接、数据采集和Python分析。通过Arduino和Python的实战案例,展示了如何从脑电波传感器获取并解析专注度、放松度等数据,适用于专注力训练和脑机接口开发。

weixin_30735745的博客 576

从UE4到Unity:双叶高光技术在角色皮肤渲染中的移植全流程

本文详细解析了将UE4中的双叶高光技术移植到Unity引擎的全流程。该技术通过模拟皮肤油脂层和真皮层的不同反射特性,能显著提升角色皮肤渲染的真实感。文章从核心原理、贴图资产处理、Shader代码重构到参数调校,提供了完整的跨引擎移植实战指南,帮助开发者实现高质量的皮肤质感还原。

alice7model的博客 479

大厂的OOM优化和监控方案

点击上方“芋道源码”,选择“设为星标”管她前浪,还是后浪?能浪的浪,才是好浪!每天 10:33更新文章,每天掉亿点点头发...源码精品专栏原创 | Java 2021超神之路,很肝~中文详细注释的开源项目RPC 框架 Dubbo 源码解析网络应用框架 Netty 源码解析消息中间件 RocketMQ 源码解析数据库中间件 Sharding-JDBC 和 MyCAT ...

芋艿V 396

让 ABAP 复杂内表优雅“喂饱”:Complex Internal Table Filling 全面实战指南

本文为ABAP开发者系统梳理了复杂内表的填充与复制方法。重点介绍了work area的两种声明方式、APPEND操作的多形态用法及其初始值语义,以及VALUE #( )构造器的逐行与整表写法。同时讲解了CORRESPONDING的同名字段映射功能,帮助开发者在"航司与航线"等复杂业务场景中实现最佳实践。文章还涵盖内存优化、可读性建议和常见误区,提供可运行样例,适用于ABAP On-Premise和SAP BTP环境。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 169

【性能优化】大厂OOM优化和监控方案

一、前言 随着项目不断壮大,OOM(Out Of Memory)成为奔溃统计平台上的疑难杂症之一,大部分业务开发人员对于线上OOM问题一般都是暂不处理,一方面是因为OOM问题没有足够的log,无法在短期内分析解决,另一方面可能是忙于业务迭代、身心疲惫,没有精力去研究OOM的解决方案。 这篇文章将以线上OOM问题作为切入点,介绍常见的OOM类型、OOM的原理、大厂OOM优化黑科技、以及主流的OOM监控方案。 文章较长,请备好小板凳~ 二、OOM问题分类 很多人对于OOM的理解就是Java虚拟机内存不足,但通过

Android23333的博客 952

Halcon那些事:Halcon非常核心的1个概念reduce_domain算子的理解和1个详细的使用示例

Reduce是减少的意思。并不是真的去裁剪图像,而是定义一个“感兴趣的区域(Region of Interest, ROI)”。后续的图像处理算子将只在这个定义的区域内有效,区域外的像素将被忽略。在一张白纸(完整图像)上放一张镂空的纸板(Domain)。我们后续的绘画(图像处理操作)只会透过镂空的部分影响到下面的白纸。镂空纸板之外的部分保持不变,也不会被处理。关键步骤:使用ReduceDomain,将图像的域限制为ROI区域,图像在原来图像基础上存在图像区域。

十多年IT经验,专注机器视觉,智能制造半导体行业 579

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

大厂干掉 OOM 的套路,你知道几个 !

文章来源:https://c1n.cn/5ug0H目录前言OOM 问题分类线程数太多打开太多文件内存不足总结前言随着项目不断壮大,OOM(Out Of Memory)成为奔溃统计平台上的疑难杂症之一。大部分业务开发人员对于线上 OOM 问题一般都是暂不处理:一方面是因为 OOM 问题没有足够的 log,无法在短期内分析解决。另一方面可能是忙于业务迭代、身心疲惫,没有精力去...

weixin_43167418的博客 374

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

asm-samples:Android 字节码插桩实战

asm-samples:Android 字节码插桩实战 项目介绍 asm-samples 是一个开源的 Android 字节码插桩项目,旨在通过示例演示字节码插桩技术在 Android 开发中的应用。项目包含多个实战案例,如双击防抖、线程整治、隐私合规、大图加载监控以及 Lambda 表达式等。这些案例可以帮助开发者更好地理解和掌握字节码插桩技术。 项目技术分析 字节码插桩是一种在编译时期对字节码...

gitblog_01064的博客 924

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

Android开发工具分析:一个使用ASM进行字节码插桩的大图监控框架

​ 到此大图监控的原理就讲解的差不多了,大家可以到我的Github上结合源码进行分析,如果觉得对您有用,可以给我点一个Star,该项目后续也会继续的进行迭代。Dokit框架以及Hunter开源库。最后大家也可以看看字节跳动开源的ByteX库,该库是一个字节码插件开发平台,集成了很多有用的插件,更多详情可以查看ByteX的文档。

Dream It Possible 271

字节码插桩库,也许有你需要的

六篇关于 Android 字节码插桩的文章,一共对应四个功能点。

chuyouyinghe的专栏 264

ASM 字节码插桩:从 Lambda 表达式讲起

字节码插桩是如今 Android 开发中非常普遍的一种技术手段,其应用范围非常广泛,涉及各种业务强关联或者和业务无关的领域,例如:无痕埋点、隐私合规检测、耗时方法统计、性能检测、双击防抖等.本篇文章再来详细介绍下在实现字节码插桩的过程中,我们难免会遇到的一个难点,也即从 Java 8 开始支持的一个新语法:Lambda 表达式,再以此扩展介绍向 Lambda 表达式进行字节码插桩的大致思路

m0_59162559的博客 740

Android 进阶篇---> App运行时大图监控

背景 最近看滴滴开源的Dokit框架中有一个大图监控的功能,可以对图片的文件大小和所占用的内存大小设置一个阈值,当图片超过该值的时候进行提示。 这个功能对于我们在做APK体积压缩,内存管理的时候还是很有用的,比如当我们要从后台返回的连接中加载一张图片,这张图片的大小我们是不知道的,虽然现在大家都使用Glide等三方 图片加载框架,框架会自动对图片进行压缩,但是依然会出现压缩后所占内存超过预期的情况。 这时候我们可以在开发、测试和预生产阶段使用大图监控来识别出那些超标的图片。 需求 在讨论如何做之前,我们必须

qq_42795723的博客 2274

反编译所有图片加载库,让OOM无所遁形!

码个蛋(codeegg) 第 989 次推文作者:ZhouZhengyi链接:https://juejin.im/post/5ea1c46851882573a25f3ec31.背景最近看...

陈宇明 526

如果App运行时大图监控,你又应该怎么做呢?

1、背景 最近看滴滴开源的Dokit框架中有一个大图监控的功能,可以对图片的文件大小和所占用的内存大小设置一个阈值,当图片超过该值的时候进行提示。 这个功能对于我们在做APK体积压缩,内存管理的时候还是很有用的,比如当我们要从后台返回的连接中加载一张图片,这张图片的大小我们是不知道的,虽然现在大家都使用Glide等三方 图片加载框架,框架会自动对图片进行压缩,但是依然会出现压缩后所占内存超过预期的情况。 这时候我们可以在开发、测试和预生产阶段使用大图监控来识别出那些超标的图片。 2、需求 在讨论如何做之前,

CHAMPION8888的博客 569

Android Bitmap内存优化实战:从原理到监控与治理

在移动应用开发中,内存管理是保障应用流畅与稳定的核心技术之一。其核心原理在于理解系统如何为不同类型的数据结构分配和回收内存,尤其是在处理图形资源时。有效的内存优化技术能显著提升应用性能,减少卡顿与崩溃,对于用户体验至关重要。在Android平台上,Bitmap作为承载图像数据的主要对象,其内存占用机制尤为关键。通过字节码插桩等技术手段,开发者可以实现对Bitmap创建与销毁过程的白盒化监控,精准定位内存异常与泄漏问题。本文聚焦于Bitmap内存的量化分析、ASM插桩监控实践以及主动优化策略,为构建高性能移动

weixin_33811539的博客 461

glide加载图片很慢_反编译所有图片加载库,让OOM无所遁形

码个蛋(codeegg) 第 989 次推文作者:ZhouZhengyi链接:https://juejin.im/post/5ea1c46851882573a25f3ec31.背景最近看滴滴开源的Dokit框架中有一个大图监控的功能,可以对图片的文件大小和所占用的内存大小设置一个阈值,当图片超过该值的时候进行提示。这个功能对于我们在做APK体积压缩,内存管理的时候还是很有用的,比如当我们要从后台返...

weixin_39869593的博客 207

引入三方库_反编译所有图片加载库,让OOM无所遁形

码个蛋(codeegg) 第 989 次推文作者:ZhouZhengyi链接:https://juejin.im/post/5ea1c46851882573a25f3ec31.背景最近看滴滴开源的Dokit框架中有一个大图监控的功能,可以对图片的文件大小和所占用的内存大小设置一个阈值,当图片超过该值的时候进行提示。这个功能对于我们在做APK体积压缩,内存管理的时候还是很有用的,比如当我们要从后台返...

weixin_42518874的博客 116
上一篇: ButterKnife为什么执行效率为什么比其他注入框架高?它的原理是什么
下一篇: Android 12 自动适配 exported 深入解析避坑
懂你的大海
博客等级 码龄6年 9049粉丝 1381原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值