Android开发[9]:组件优化之包体积+内存

6 阅读9分钟

Android组件优化之包体积+内存优化

今日目标

  • 掌握组件化项目2大核心性能优化方向(包体积优化+内存优化),精通每种优化方向的实战方法、工具使用和职场选型逻辑。
  • 完成分包规范后项目的性能优化实战,规避性能优化相关的高频踩坑点,提升工程运行效率、降低崩溃率。

组件化性能优化

价值

  • 解决组件化项目包体积过大、启动速度慢、内存泄漏、卡顿崩溃等问题。
  • 提升App运行流畅、降低崩溃率、减少用户流失,降低运维成本和流量消耗。

2大优化方向

  • 包体积优化
  • 内存优化
包体积优化
  • 核心:精简冗余、压缩资源。
  • 降低App安装包大小、提升下载转化率。
  • 适配低端设备和流量敏感场景。
优化工具
  • Android Studio自带Build Analyzer(分析包体积构成)。
  • Lint(检测无用资源)。
  • R8/ProGuard(混淆压缩)。
  • TinyPNG(图片压缩)。
内存优化
  • 核心:减少内存占用、避免内存泄漏。
  • 提升App运行稳定性,减少卡顿、ANR和崩溃。
  • 适配低端机设备。
优化工具
  • Android Studio自带Profiler(内存监控+泄漏检测)。
  • LeakCanary(轻量级内存泄漏检测)。

实战

  • 包体积优化
  • 内存优化
准备工作
  • 1.分析性能瓶颈
  • 包体积分析
    • 使用Android Studio Build Analyzer,查看apk包构成(jar、资源文件、代码等),定位冗余模块。
    • 步骤:Build -> Analyze APK... -> 选择分析的apk,查看各部分大小
  • 内存分析
    • 使用Android Studio Profiler,运行壳工程,监控内存占用、GC频率,初步定位内存泄漏和内存占用过高的模块。
    • 步骤:View -> Tool Windows -> Profiler,启动APP,排查异常。
  • 明确优化目标:精简包体积,降低内存占用,无内存泄漏,优化后App启动速度提升。
  • 2.准备优化工具
  • 内置工具
    • Android Studio Build Analyzer、Profiler、Lint、R8/ProGuard
  • 三方工具
    • 配置LeakCanary至项目中(内存泄漏检测),仅debug引入
  • 3.统一优化标准:提前约定,避免混乱
  • 包体积优化标准:删除无用资源、清理冗余依赖、压缩图片资源,不影响功能和用户体验
  • 内存优化标准:无内存泄漏、内存占用稳定、GC频率降低,APP运行无卡顿、无ANR
  • 优化优先级:核心功能相关代码/资源不轻易优化 → 低成本优化优先(例:删除无用资源) → 高成本优化延后(例:代码重构)
包体积优化

聚焦“冗余清理、资源压缩、依赖优化”。

  • 1.清理无用资源
  • 使用Lint检测无用资源
    • 步骤:项目右键Analyze → Run Inspection by Name... → 搜索“Unused Resources” → 选择整个工程 → 执行检测
  • 删除无用资源
    • 重点删除:未使用的图片、布局文件、字符串、样式、drawable资源
    • 注意:分包后各组件的无用资源单独检测、单独删除(base组件无用资源、user组件无用资源分开处理)
    • 禁止:删除核心资源(例:BaseActivity布局、登录相关资源),避免功能退化
  • 配置自动移除无用资源(release模式),在壳工程build.gradle中添加
buildTypes {
    release {
        isMinifyEnabled = true

        isShrinkResources = true // 自动移除无用资源,与混淆配套使用
            
        proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
    }

    debug {
        isMinifyEnabled = false
    }
}
  • 2.资源压缩优化
  • 图片资源压缩
    • 使用TinyPNG压缩所有图片(drawable、mipmap中的图片),压缩比例50%-70%,不影响视觉效果
    • 使用webp图片资源
    • 适配方案:删除高清冗余图片(例:仅保留xxhdpi、hdpi,低端设备自动适配),避免多分辨率重复资源
  • 资源混淆,在壳工程proguard-rules.pro中添加资源混淆规则,缩短资源名称
  • 3.依赖优化(清理冗余,避免重复引入)
  • 清理冗余依赖
    • 使用Build Analyzer检测重复依赖(例:重复引入的jar包、第三方库)
    • 重点清理:base组件已暴露的依赖(如ARouter、MMKV),user组件无需重复引入
  • 依赖按需引入
    • 第三方依赖仅引入所需模块,避免引入完整包(如Glide仅引入核心模块,而非全量)
    • 清理Version Catalog中未使用的依赖版本,确保依赖统一、无冗余
  • 替换重型依赖
    • 若项目中有重型依赖(例:Glide),替换为轻量级替代方案(例:Coil),或仅引入所需模块
  • 4.代码压缩优化(配合混淆,进一步精简)
  • 强化混淆配置
    • 确保R8/ProGuard混淆正常,清理无用代码(minifyEnabled true)
    • 补充混淆规则,确保核心代码不被混淆(如base组件tools分包、service分包,user组件核心业务代码)
  • 删除无用代码
    • 检测并删除未使用的方法、变量、类(Android Studio会提示“Unused”)
    • 重点清理:分包重构后遗留的无用代码、注释掉的代码、测试代码
  • 5.验证包体积优化效果
  • 同步项目,打包release版本,对比优化前后的包体积
  • 运行release包,测试所有功能,确保ARouter跳转、Hilt注入、数据共享等功能正常
  • 验证要点:包体积精简达标,功能无退化,资源加载正常,无资源找不到报错
内存优化

聚焦“内存泄漏排查与修复、内存占用优化、缓存管理”。

  • 1.集成LeakCanary,排查内存泄漏
  • 准备阶段已配置LeakCanary依赖,同步工程后,运行debug模式工程
  • 步骤:启动APP → 跳转各页面(MainActivity→LoginActivity→UserActivity)→ 退出页面 → 观察LeakCanary通知
  • 常见内存泄漏场景及修复
    • 场景1:Activity未及时销毁,被静态变量引用(例:GlobalDataManager中持有Activity实例)
      • 修复:使用弱引用(WeakReference)持有Activity实例,避免强引用
    • 场景2:组件生命周期未释放资源(例:UserComponentLifecycle未取消Flow监听)
      • 修复:在onUnload方法中取消所有监听、清空缓存
    • 场景3:Handler内存泄漏(例:项目中有Handler,但未使用静态内部类)
      • 修复
        • 使用静态内部类+弱引用持有Activity,避免Handler持有Activity实例
        • 页面销毁时,移除所有消息,避免内存泄漏
// 示例

// 场景一:Activity未及时销毁,被静态变量引用(例:GlobalDataManager中持有Activity实例)
@Singleton
class GlobalDataManager @Inject constructor() {
    // 弱引用持有Activity实例,避免内存泄漏
    private var currActivity: WeakReference<Activity>? = null

    fun setCurrActivity(activity: Activity) {
        currActivity = WeakReference(activity)
    }

    fun getCurrentActivity(): Activity? = currActivity?.get()
}

// 场景二:组件生命周期未释放资源(例:UserComponentLifecycle未取消Flow监听)
class UserComponentLifecycle @Inject constructor(): IComponentLifecycle {
    override fun onLoad() {
        // 加载资源
    }

    override fun onUnLoad() {
        // 释放资源
    }
}

// 场景三:Handler内存泄漏(例:项目中有Handler,但未使用静态内部类)
class MainActivity : BaseActivity() {
    ...

    // 静态内部类Handler,弱引用持有Activity
    private val mHandler = MyHandler(this)

    // 静态内部类
    private class MyHandler(activity: MainActivity) : Handler(Looper.getMainLooper()) {
        private val mActivity: WeakReference<MainActivity> = WeakReference(activity)

        override fun handleMessage(msg: Message) {
            val activity = mActivity.get()
            activity?.let {
                // 处理消息,避免直接引用Activity
            }
        }
    }

    // 页面销毁时,移除所有消息,避免内存泄漏
    override fun onDestroy() {
        super.onDestroy()
        mHandler.removeCallbacksAndMessages(null)
    }
    
    ...
}
  • 2.优化内存占用,减少不必要的内存消耗
  • 图片内存优化
    • 避免加载过大图片,按需加载(例:列表图片加载缩略图,点击查看或下载使用原图)
    • 及时释放图片资源(例:页面销毁时,清空ImageView的Bitmap图片)
  • 缓存优化
    • 合理设置缓存大小,避免缓存过大占用内存
    • 及时清理无用缓存(例:App退至后台清理临时缓存)
  • 避免内存浪费
    • 减少静态变量的使用,避免静态集合持有大量数据
    • 使用轻量级数据结构(例:ArrayList替代LinkedList,避免不必要的内存占用)
    • 避免重复创建对象(例:在onDraw、循环方法中创建对象,改为全局复用)
// 示例
override fun onDestroy() {
    super.onDestroy()

    // 释放图片资源
    findViewById<ImageView>(R.id.iv_user_avatar).setImageBitmap(null)
}
  • 3.验证内存优化效果
  • 运行项目,使用Profiler监控内存占用,对比优化前后的内存曲线,确保内存占用降低
  • 使用LeakCanary检测,确保无内存泄漏(无LeakCanary通知)
  • 反复跳转各页面、退出页面,观察App运行状态,确保无卡顿、无ANR、无崩溃
  • 验证要点:内存占用稳定,GC频率降低,无内存泄漏,功能正常

踩坑点

坑点1:包体积优化后,出现“资源找不到”报错

  • 原因
    • 误删核心资源
    • 资源混淆配置不当
    • shrinkResources误删有用资源
  • 修复
    • 排查删除的资源,恢复核心资源
    • 优化资源混淆规则
    • 在shrinkResources中保留必要资源(如res/raw、特定图片),添加keep规则。

坑点2:LeakCanary检测到内存泄漏,但无法定位原因

  • 原因:未掌握内存泄漏场景,未结合组件生命周期排查
  • 修复:结合Profiler进一步分析内存快照,重点排查静态变量、Handler、生命周期未释放资源等场景,对照实战案例修复。

坑点3:内存优化后,APP出现卡顿

  • 原因
    • 过度优化(例:频繁释放资源、重复创建对象)
    • 图片加载逻辑不合理
  • 修复
    • 避免过度优化,合理复用对象
    • 优化图片加载逻辑,按需加载、异步加载,避免主线程阻塞

坑点4:依赖优化后,出现依赖缺失报错

  • 原因
    • 误删必要依赖
    • 依赖暴露方式错误(例:base组件公共依赖用implementation而非api)
  • 修复
    • 恢复必要依赖
    • 调整base组件依赖暴露方式,公共依赖用api,内部依赖用implementation。

坑点5:release模式下,优化后功能失效

  • 原因:混淆配置不当,核心代码被混淆
  • 修复:补充混淆规则,保留base组件tools、service分包,user组件核心业务代码,确保ARouter、Hilt、MMKV等相关类不被混淆

坑点6:包体积精简不达标

  • 原因:未彻底清理冗余依赖、图片未压缩、无用资源未完全删除
  • 修复:重新使用Build Analyzer、Lint检测,清理遗漏的冗余依赖和无用资源;重新压缩所有图片,优化依赖引入方式