内存泄漏(Memory Leak)是指应用中不再使用的对象因错误引用无法被垃圾回收(GC),导致内存占用持续增长,最终可能引发 OOM(Out Of Memory)崩溃 或 应用卡顿。以下是 Android 内存泄漏的优化方案,涵盖检测工具、常见场景及解决方案。
一、内存泄漏检测工具
1、Android Profiler(Android Studio 自带)
- 操作步骤 :
View > Tool Windows > Profiler
→ 选择内存分析 → 查看内存分配和泄漏对象。 - 关键功能:实时监控内存分配,支持捕获堆转储(Heap Dump)分析对象引用链。
2、LeakCanary(第三方库)
- 集成方式:
gradle
dependencies {
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.12'
}
- 功能:自动检测内存泄漏并生成可视化报告,直接定位泄漏对象。
3、MAT(Memory Analyzer Tool)
使用流程:
- 通过 Android Profiler 导出堆转储文件(
.hprof
)。 - 使用 MAT 分析文件,通过 Dominator Tree 查找大对象或重复对象。
二、常见内存泄漏场景与代码示例
1、静态引用 Activity/Context
错误代码:
kotlin
class AppManager {
companion object {
var activity: Activity? = null // 静态持有 Activity
}
}
// 在 Activity 中赋值
AppManager.activity = this
泄漏原因:静态变量生命周期长于 Activity,导致 Activity 无法释放。
解决方案:
kotlin
// 使用静态内部类 + 弱引用
class MyActivity : Activity() {
private class SafeHandler(activity: MyActivity) : Handler(Looper.getMainLooper()) {
private val weakActivity = WeakReference(activity)
override fun handleMessage(msg: Message) {
weakActivity.get()?.handleMessage(msg)
}
}
private val handler = SafeHandler(this)
}
2、匿名内部类(Handler、Runnable)
错误代码:
kotlin
class MyActivity : Activity() {
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
// 引用 Activity
}
}
}
泄漏原因:匿名内部类隐式持有外部类(Activity)引用。
解决方案:
kotlin
// 使用静态内部类 + 弱引用
class MyActivity : Activity() {
private class SafeHandler(activity: MyActivity) : Handler(Looper.getMainLooper()) {
private val weakActivity = WeakReference(activity)
override fun handleMessage(msg: Message) {
weakActivity.get()?.handleMessage(msg)
}
}
private val handler = SafeHandler(this)
}
3、匿名内部类(Handler、Runnable)
错误代码:
kotlin
class MyActivity : Activity() {
override fun onCreate() {
super.onCreate()
registerReceiver(receiver, IntentFilter("MY_ACTION"))
}
private val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {}
}
}
泄漏原因 :未在 onDestroy()
中解注册 BroadcastReceiver
。
解决方案:
kotlin
override fun onDestroy() {
unregisterReceiver(receiver)
super.onDestroy()
}
4、单例模式持有 Context
错误代码:
kotlin
class Singleton private constructor(context: Context) {
companion object {
private var instance: Singleton? = null
fun getInstance(context: Context): Singleton {
if (instance == null) {
instance = Singleton(context.applicationContext) // 使用应用上下文
}
return instance!!
}
}
}
正确实践 :单例中应使用 Application Context
,避免持有 Activity Context。
5、资源未关闭(Cursor、File、Bitmap)
错误代码:
kotlin
fun loadBitmap(): Bitmap {
val options = BitmapFactory.Options()
return BitmapFactory.decodeResource(resources, R.drawable.large_image, options)
}
泄漏原因:未及时回收大图资源。
解决方案:
kotlin
override fun onDestroy() {
bitmap?.recycle()
super.onDestroy()
}
三、优化策略总结
1、避免静态引用 Activity:
- 使用
Application Context
替代Activity Context
。 - 静态对象通过
WeakReference
持有 Activity。
2、正确管理生命周期:
- 在
onDestroy()
中移除Handler
消息、注销监听器、关闭资源(如数据库、文件流)。
3、优化匿名内部类:
- 将匿名内部类改为静态内部类,通过弱引用持有外部类。
4、单例模式注意事项:
- 使用
Application Context
初始化单例。 - 避免单例直接持有 UI 相关对象。
5、工具辅助检测:
- 集成
LeakCanary
自动化检测。 - 定期使用 Android Profiler 分析内存。
四、高级技巧
1、避免 ViewModel 泄漏
- 错误代码:在 ViewModel 中直接持有 Activity 引用。
- 解决方案 :使用
AndroidViewModel
或通过Application Context
访问资源。
2、使用 WeakHashMap
- 场景:缓存需要自动清理的对象。
kotlin
private val cache = WeakHashMap<Key, WeakReference<Bitmap>>()
3、监控 Fragment 泄漏
- 常见问题:Fragment 因被后台线程持有而无法销毁。
- 解决方案 :在
onDestroyView()
中清空 Fragment 的视图引用。
五、其他注意事项
- 避免在
onDraw()
中创建对象:频繁调用的方法中创建对象易引发内存抖动。 - 谨慎使用第三方库:某些库可能隐式持有 Context,需确认其生命周期管理。
- 定期代码审查:重点关注静态变量、集合类、监听器注册等场景。
六、总结
内存泄漏优化是 Android 性能调优的核心环节。通过 工具检测 + 代码规范 + 架构设计 的综合手段,可有效减少泄漏风险。关键点总结:
- 预防为主:编码时遵循生命周期管理最佳实践。
- 及时检测:集成 LeakCanary 和 Profiler,定期分析堆内存。
- 修复闭环:根据工具报告定位问题,验证修复效果。