# Android 常见内存泄漏

Android 常见内存泄漏与避免

一、常见泄漏场景与处理

1.1 Activity / Fragment 被静态或长生命周期对象持有

场景:单例、静态变量、Application 等持有 Activity/Fragment 引用。

kotlin 复制代码
// ❌ 错误
object Singleton {
    var activity: Activity? = null  // 静态持有 Activity
}

// ✅ 正确
object Singleton {
    private var activityRef: WeakReference<Activity>? = null
    fun setActivity(activity: Activity) {
        activityRef = WeakReference(activity)
    }
}

避免 :用 WeakReference 或确保在 onDestroy 中置空引用。


1.2 非静态内部类 / 匿名内部类持有外部类

场景:Handler、Runnable、Thread、AsyncTask 等持有 Activity。

kotlin 复制代码
// ❌ 错误:匿名 Runnable 持有 Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Handler(Looper.getMainLooper()).postDelayed({
            findViewById<View>(R.id.text).text = "done"  // 持有 this(MainActivity)
        }, 10000)
    }
}

// ✅ 正确:静态内部类 + 弱引用,或在 onDestroy 中 removeCallbacks
class MainActivity : AppCompatActivity() {
    private val handler = Handler(Looper.getMainLooper())
    override fun onDestroy() {
        handler.removeCallbacksAndMessages(null)  // 移除所有回调,避免泄漏
        super.onDestroy()
    }
}

避免 :用静态内部类 + WeakReference,或在 onDestroy 中移除回调和消息。


1.3 监听器未反注册

场景BroadcastReceiverEventBusLiveData、自定义监听器等未在 onDestroy 中注销。

kotlin 复制代码
// ❌ 错误
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        LocalBroadcastManager.getInstance(this).registerReceiver(receiver, filter)
        EventBus.getDefault().register(this)
    }
}

// ✅ 正确
override fun onDestroy() {
    LocalBroadcastManager.getInstance(this).unregisterReceiver(receiver)
    EventBus.getDefault().unregister(this)
    super.onDestroy()
}

避免 :在 onDestroy 中对称注销所有注册的监听器。


1.4 协程持有 Activity 或 Context

场景 :在 lifecycleScopeviewModelScope 外启动协程,且持有 Activity 引用。

kotlin 复制代码
// ❌ 错误:GlobalScope 协程持有 Activity
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        GlobalScope.launch {
            delay(5000)
            runOnUiThread { updateUI() }  // 持有 this(MainActivity)
        }
    }
}

// ✅ 正确:使用 lifecycleScope,自动取消
lifecycleScope.launch {
    delay(5000)
    updateUI()
}

避免 :用 lifecycleScopeviewModelScope 等,避免 GlobalScope 和长时间持有 Activity。


1.5 单例持有 Context

场景:单例、工具类持有 Activity 或 Context。

kotlin 复制代码
// ❌ 错误
object ImageLoader {
    private var context: Context? = null  // 若传入 Activity,会泄漏
    fun init(ctx: Context) { context = ctx }
}

// ✅ 正确:使用 ApplicationContext
fun init(ctx: Context) {
    context = ctx.applicationContext
}

避免 :用 applicationContext,不要用 Activity 作为 Context 传给单例。


1.6 View 持有 Activity

场景:自定义 View 或 Adapter 持有 Activity 引用。

kotlin 复制代码
// ❌ 错误
class MyAdapter(private val activity: Activity) : RecyclerView.Adapter<...>()

// ✅ 正确:传 Context 或 Fragment
class MyAdapter(private val context: Context) : RecyclerView.Adapter<...>()

避免 :尽量传 Context 或 Fragment,避免直接持有 Activity。


1.7 资源未关闭

场景CursorInputStreamOutputStreamSocketOkHttp Response.body 等未关闭。

kotlin 复制代码
// ❌ 错误
val response = call.execute()
val body = response.body?.string()  // 未关闭,连接无法复用

// ✅ 正确
response.use {
    it.body?.use { body ->
        val str = body.string()
    }
}

避免 :用 usetry-finally 确保资源关闭。


1.8 第三方库回调持有 Activity

场景:Glide、Retrofit、OkHttp 等回调在 Activity 销毁后仍可能被调用。

kotlin 复制代码
// ❌ 错误
call.enqueue(object : Callback {
    override fun onResponse(call: Call, response: Response) {
        // 若 Activity 已销毁,可能仍持有 Activity
        textView.text = response.body().toString()
    }
})

// ✅ 正确:在 onDestroy 中 cancel
private var call: Call? = null
override fun onDestroy() {
    call?.cancel()
    super.onDestroy()
}

避免 :在 onDestroy 中取消请求,避免回调持有 Activity。


1.9 定时器未取消

场景TimerHandler.postDelayedScheduledExecutorService 等在 Activity 销毁后仍运行。

kotlin 复制代码
// ✅ 正确
private val handler = Handler(Looper.getMainLooper())
private val runnable = Runnable { /* ... */ }

override fun onDestroy() {
    handler.removeCallbacks(runnable)
    super.onDestroy()
}

避免 :在 onDestroy 中移除回调和消息。


1.10 闭包 / Lambda 持有外部引用

场景 :Kotlin 中 lambda 捕获了 this 或 Activity。

kotlin 复制代码
// ❌ 错误
view.setOnClickListener {
    // 持有 Activity
    startActivity(Intent(this@MainActivity, OtherActivity::class.java))
}

// 若 lambda 被传给会长期持有的对象,需注意

避免:注意 lambda 捕获的引用,避免被长生命周期对象持有。


二、排查与规避建议

手段 说明
LeakCanary 在 debug 中接入,自动检测 Activity 等泄漏
弱引用 单例、静态变量引用 Activity 时用 WeakReference
生命周期 onDestroy 中置空引用、取消注册、取消任务
ApplicationContext 单例、工具类使用 applicationContext
结构化并发 使用 lifecycleScopeviewModelScope 等,避免 GlobalScope
资源 使用 usetry-finally 确保资源关闭

三、检查清单

检查项 说明
单例/静态变量 是否持有 Activity?改用 WeakReference 或 applicationContext
Handler / Runnable 是否在 onDestroy 中 removeCallbacksAndMessages?
监听器 BroadcastReceiver、EventBus 等是否在 onDestroy 中 unregister?
协程 是否用 lifecycleScope 而非 GlobalScope?
网络请求 是否在 onDestroy 中 cancel?
资源 Cursor、Stream、Response.body 是否关闭?
相关推荐
BY组态5 分钟前
Ricon组态系统技术深度解析:打造高性能Web可视化平台
前端·物联网·iot·web组态·组态
山屿落星辰19 分钟前
Flutter 高级特性实战:动画、自定义绘制、平台通道与 Web 优化
前端·flutter
@菜菜_达1 小时前
jquery.inputmask插件介绍
前端·javascript·jquery
QuZhengRong1 小时前
【Luck-Report】缓存
java·前端·后端·vue·excel
jiayong231 小时前
前端面试题库 - 浏览器与网络篇
前端·网络·面试
Csvn1 小时前
小程序开发:微信小程序与 uni-app 实战指南
前端
摸鱼小李上线了1 小时前
vue项目页面添加水印实现方法
前端·javascript·vue.js
砍材农夫1 小时前
物联网 基于netty构建mqtt协议规范(主题通配符订阅)
java·前端·javascript·物联网·netty
彩票管理中心秘书长2 小时前
智能体状态指示:何时思考、何时调用工具、何时出错
前端·后端·程序员
彩票管理中心秘书长2 小时前
React + TypeScript拆解一整套“AI 变现代码流程”
前端·后端·程序员