Skip to content

OnClickListener2048/BigImageDetector

Repository files navigation


BigImageDetector Plugin

一个基于 Android ASM 字节码插桩的 Gradle 插件,用于在开发阶段自动检测应用中加载的大图(图片尺寸远大于显示尺寸),帮助开发者定位和优化图片内存使用问题。

🚀 特性

  • 无侵入式集成: 无需修改任何业务代码或 XML 布局,通过 Gradle 插件自动实现。
  • 编译时插桩: 利用 ASM 在编译期间对字节码进行操作,对运行时性能影响极小。
  • 覆盖广泛: 能够监控所有通过 ImageView.setImageBitmap()ImageView.setImageDrawable() 设置图片的场景,包括 Glide、Picasso 等主流图片加载库的最终调用。
  • 环境隔离: 默认只在 debug 构建模式下生效,对 release 包无任何影响。
  • 清晰的告警: 通过 Logcat 输出详细的警告信息,包括图片原始尺寸、View 显示尺寸,方便快速定位问题。

🧐 原理

本插件的核心原理是编译时字节码插桩(Compile-time Bytecode Instrumentation)

  1. Gradle 插件机制: 通过自定义 Gradle 插件,我们可以接入 Android Gradle Plugin (AGP) 的构建流程。
  2. ASM 字节码操作: 利用强大的 ASM 库,插件在 .class 文件转换为 .dex 文件的过程中,对字节码进行扫描和修改。
  3. 方法拦截: 插件会遍历所有方法,并找到所有调用 android.widget.ImageViewsetImageBitmap()setImageDrawable() 方法的地方。
  4. 代码织入 (Weaving): 在这些方法调用点之前,插件会动态地插入一段我们自己的代码。这段代码会:
    • 复制栈顶的 ImageView 实例和要设置的 BitmapDrawable 对象。
    • 调用一个预先写好的运行时辅助类(BigImageDetector.check())并传入这些对象。
  5. 运行时检测: 当应用在 Debug 模式下运行时,每次有图片被设置到 ImageView,我们注入的代码就会被触发。
    • BigImageDetector 会在一个安全的时机(imageView.post())获取 ImageView 的实际显示尺寸和 Bitmap 的原始尺寸。
    • 如果 Bitmap 尺寸显著大于 ImageView 的显示尺寸(默认阈值为 2 倍),就在 Logcat 中打印一条警告日志。

🛠️ 如何使用

这个插件设计为通过 buildSrc 目录集成到你的 Android 项目中。

步骤 1: 在项目中创建运行时辅助类

在你的 Android app 模块的源代码中(例如 app/src/main/java/com/yourpackage/performance/),创建 BigImageDetector.kt 文件。

// app/src/main/java/com/yourpackage/performance/BigImageDetector.kt
package com.yourpackage.performance

import android.graphics.Bitmap
import android.graphics.drawable.BitmapDrawable
import android.graphics.drawable.Drawable
import android.util.Log
import android.view.View
import android.widget.ImageView

object BigImageDetector {
    private const val TAG = "BigImageDetector"
    private const val SIZE_THRESHOLD = 2.0 // 告警阈值

    @JvmStatic
    fun check(view: View?, drawable: Drawable?) {
        if (view !is ImageView || drawable == null) return
        if (drawable !is BitmapDrawable) return
        val bitmap = drawable.bitmap ?: return
        checkBitmap(view, bitmap)
    }

    @JvmStatic
    fun check(view: View?, bitmap: Bitmap?) {
        if (view == null || bitmap == null) return
        checkBitmap(view as ImageView, bitmap)
    }

    private fun checkBitmap(imageView: ImageView, bitmap: Bitmap) {
        imageView.post {
            val viewWidth = imageView.width
            val viewHeight = imageView.height

            if (viewWidth <= 0 || viewHeight <= 0) return@post

            val bitmapWidth = bitmap.width
            val bitmapHeight = bitmap.height

            if (bitmapWidth > viewWidth * SIZE_THRESHOLD &&
                bitmapHeight > viewHeight * SIZE_THRESHOLD
            ) {
                val warningMessage = "大图警告: " +
                        "图片尺寸 ($bitmapWidth x $bitmapHeight) " +
                        "远大于显示尺寸 ($viewWidth x $viewHeight)"
                Log.w(TAG, warningMessage)
            }
        }
    }
}

步骤 2: 创建 buildSrc 模块和插件代码

在你的项目根目录下,创建 buildSrc 目录,并配置以下文件:

buildSrc/build.gradle.kts

import org.jetbrains.kotlin.gradle.tasks.KotlinCompile

repositories {
    google()
    mavenCentral()
}

plugins {
    `kotlin-dsl`
}

dependencies {
    implementation("com.android.tools.build:gradle:7.0.4") // 替换为你项目的 AGP 版本
    implementation("org.ow2.asm:asm:9.2")
    implementation("org.ow2.asm:asm-commons:9.2")
}

tasks.withType<KotlinCompile> {
    kotlinOptions.jvmTarget = "1.8"
}

buildSrc/src/main/resources/META-INF/gradle-plugins/com.yourcompany.big-image-detector.properties

implementation-class=com.yourcompany.plugin.BigImageDetectorPlugin

buildSrc/src/main/kotlin/com/yourcompany/plugin/BigImageDetectorPlugin.kt (以及其他插件类)

将之前提供的四个插件 Kotlin 文件 (BigImageDetectorPlugin.kt, BigImageDetectorClassVisitorFactory.kt, BigImageDetectorClassAdapter.kt, BigImageDetectorMethodAdapter.kt) 放置到 buildSrc/src/main/kotlin/com/yourcompany/plugin/ 目录下。

确保 BigImageDetectorPlugin.kt 包含只在 debug 模式下生效的逻辑。

步骤 3: 在 app 模块中应用插件

在你的 app/build.gradle.kts 文件中,通过插件 ID 应用插件。

// android/app/build.gradle.kts

plugins {
    id("com.android.application")
    id("org.jetbrains.kotlin.android")
    // 应用我们自定义的插件
    id("com.yourcompany.big-image-detector") 
}
// ...

步骤 4: 同步 Gradle 并运行

  1. 修改 buildSrc 后,Android Studio 会提示你同步 Gradle。点击 Sync Now
  2. Debug 模式运行你的应用。
  3. 操作应用,加载图片。
  4. 打开 Logcat,并使用 BigImageDetector 作为 TAG 进行过滤。如果存在大图加载,你将看到类似以下的警告信息:
W/BigImageDetector: 大图警告: 图片尺寸 (2048x1536) 远大于显示尺寸 (400x300)

⚙️ 注意事项

  • 仅限 Debug: 本插件设计为仅在 debug 构建下工作,不会对 release 包产生任何影响。
  • 性能: 虽然插件本身经过优化,但字节码插桩会略微增加编译时间。
  • cacheWidth/cacheHeight 发现大图后,最佳的优化方式通常是使用 Image Widget(如 Glide、Coil、Image.network)提供的 cacheWidth/cacheHeight 或类似的缩放参数,让图片库在解码时就将图片缩放到合适的尺寸。

About

ASM大图检测工具

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages