Android开发[10]:性能优化之内存

Android性能优化

今日目标

  • 吃透Android常见内存泄漏的场景、核心成因,掌握通用修复套路,快速识别项目中潜在的泄漏风险。
  • 熟练集成LeakCanary,能看懂泄漏堆栈、定位泄漏点、完成修复。
  • 掌握主线程卡顿的核心成因、ANR触发条件,牢记日常开发避坑规范,能剥离主线程耗时代码。
  • 主攻「LeakCanary内存泄漏排查 + 主线程卡顿优化 + 工程性能自查」

高频内存泄漏场景

  • 核心:内存泄漏 = 长生命周期对象持有短生命周期对象
    • 例:Activity、Fragment的引用,导致短生命周期对象无法被GC回收,长期积累引发OOM

场景1:静态变量持有Activity/Context

  • 原因:静态变量生命周期与应用一致,持有页面引用后,页面销毁仍无法释放。
  • 修复:使用弱引用(WeakReference),页面销毁时置空静态变量。
kotlin 复制代码
class UserActivity: BaseActivity() {
    private val binding: ActivityUserBinding by lazy { ActivityUserBinding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        ...
        sActivity = WeakReference(this)
    }
    
    ...

    override fun onDestroy() {
        super.onDestroy()
        
        // 页面销毁时清空弱引用
        sActivity?.clear()
        sActivity = null
    }

    companion object {
        // 弱引用
        private var sActivity: WeakReference<UserActivity>? = null
    }
}

场景2:匿名内部类/lambda隐式持有外部类

  • 原因:匿名内部类默认持有外部页面引用,若内部类做耗时操作(例:线程、延时任务),页面销毁后仍在执行,导致泄漏。
  • 修复:使用静态内部类+弱引用,或页面销毁时取消耗时操作。

场景3:Handler延时消息未移除

  • 原因:Handler发送延时消息后,消息队列持有Handler引用,Handler又持有Activity引用,页面销毁后消息未移除,导致泄漏。
  • 修复:页面onDestroy时,调用handler.removeCallbacksAndMessages(null)。
kotlin 复制代码
class UserActivity: BaseActivity() {
    private val binding: ActivityUserBinding by lazy { ActivityUserBinding.inflate(layoutInflater) }
    
    ...

    private val mHandler = object : Handler(Looper.getMainLooper()) {
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)

            // 模拟耗时操作
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        ...

        // 发送10s延时消息,页面销毁后消息仍在队列
        mHandler.sendEmptyMessageDelayed(1, 10000)
    }

    override fun onDestroy() {
        super.onDestroy()

        ...
        
        // 移除所有消息和回调,避免泄露
        mHandler.removeCallbacksAndMessages(null)
    }
}

场景4:单例持有页面引用

  • 原因:单例是全局唯一、生命周期长,若构造方法或方法参数传入Activity,会长期持有引用。
  • 修复:单例中使用弱引用持有页面,或避免直接传入Activity,改用Application Context。

场景5:定时器、协程生命周期未取消

  • 原因:页面销毁后,定时器(Timer)、协程仍在运行,持有页面引用。
  • 修复:页面onDestroy时,取消定时器、取消协程(job.cancel())。
kotlin 复制代码
// 使用lifecycleScope,自动跟随页面生命周期取消
class UserActivity : BaseActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        ...

        // lifecycleScope 会在页面销毁时自动取消协程,无需手动取消
        lifecycleScope.launch {
            // 耗时操作逻辑
            delay(10000)
        }
    }
}

场景6:自定义View、监听未解绑

  • 原因:页面销毁时,未解除自定义View的监听、广播接收器、EventBus订阅等,导致引用持有。
  • 修复:页面onDestroy时,解绑监听、取消订阅。

场景7:资源未释放

  • 原因:Bitmap、文件流、MediaPlayer等资源使用后未关闭/释放,占用内存且无法回收。
  • 修复:使用后及时close()、recycle(),结合try-catch-finally确保释放。

LeakCanary集成与使用

  • 1.依赖版本配置
ini 复制代码
// gradle/libs.versions.toml
[versions] # 版本号
...
leakcanary = "2.12"

[libraries] # 依赖库
...
leakcanary = { group = "com.squareup.leakcanary", name = "leakcanary-android", version.ref = "leakcanary" }
  • 2.两种引入方式(2选1即可):全项目监控
scss 复制代码
// 方式1:base/build.gradle.kts
dependencies {
    debugApi(libs.leakcanary)
}

// 方式2:app/build.gradle.kts
dependencies {
    debugImplementation(libs.leakcanary)
}
  • 3.LeakCanary版本2.x之后只需引入依赖即可自动监控

卡顿 & ANR

卡顿是主线程阻塞导致,长期严重卡顿会触发ANR,影响用户体验,是线上App质量考核的核心指标。

  • ANR触发条件:主线程阻塞超过5秒、广播接收者执行超过10秒、服务执行超过20秒(前台)/200秒(后台)。
  • 主线程禁止操作:网络请求、文件IO(读写)、大数据解析(JSON、Excel解析)、多层循环耗时操作、复杂计算。
  • 卡顿优化核心:将主线程耗时代码剥离到子线程(使用协程、ThreadPoolExecutor),避免同步调用。
  • 辅助优化:布局优化(减少嵌套、避免过度绘制、使用ViewStub懒加载)、启动优化(冷启动异步初始化、非核心任务懒加载)。

项目性能自查

  • 内存自查:使用Android Studio Profiler(Memory面板),查看APP运行时内存占用,排查是否有内存抖动、内存泄漏。
  • 卡顿自查:使用Profiler(CPU面板),查看主线程CPU占用,定位耗时操作,确保无长期阻塞。
  • 启动速度自查:记录APP冷启动、热启动时间,排查启动时主线程耗时任务,优化异步初始化。
相关推荐
pyz6661 小时前
Retrofit 源码分析
android·retrofit
xiaoduzi19911 小时前
Android 线程池总结
android
YIN_尹1 小时前
【Linux系统编程】基础IO第二讲——文件描述符
android·linux·服务器
像风一样自由20202 小时前
量化压缩实战:INT8 / INT4 / AWQ / GPTQ 全面对比
android·人工智能·语言模型·大模型
brycegao3212 小时前
Android MVI进阶:纯原生实现Slot化可插拔架构
android·kotlin·架构设计·mvi·viewmodel
2601_961194023 小时前
27考研资料|百度网盘|夸克网盘
android·xml·考研·ios·iphone·xcode·webview
故渊at3 小时前
第二板块:Android 四大组件标准化学理 | 第十篇:ContentProvider 数据共享与 SQLite 引擎
android·jvm·数据库·sqlite·contentprovider
Kapaseker3 小时前
你遇到过 Kotlin 协程中的竞争问题吗?
android·kotlin
与水同流3 小时前
Android13 AIDL HAL服务实现Demo
android·hal·aidl