# 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 是否关闭?
相关推荐
Jagger_1 小时前
周末和AI肝了两天,终于知道:为什么要把AI当做实习生
前端
weixin_456164831 小时前
vue3 子组件向父组件传参
前端·vue.js
沉鱼.441 小时前
第十二届题目
java·前端·算法
Setsuna_F_Seiei1 小时前
CocosCreator 游戏开发 - 多维度状态机架构设计与实现
前端·cocos creator·游戏开发
Bigger1 小时前
CodeWalkers:让 AI 助手化身桌面宠物,陪你敲代码的赛博伙伴!
前端·app·ai编程
cyclv3 小时前
无网络地图展示轨迹,地图瓦片下载,绘制管线
前端·javascript
土豆12503 小时前
Tauri 入门与实践:用 Rust 构建你的下一个桌面应用
前端·rust
小陈工4 小时前
2026年4月2日技术资讯洞察:数据库融合革命、端侧AI突破与脑机接口产业化
开发语言·前端·数据库·人工智能·python·安全
IT_陈寒5 小时前
Vue的这个响应式问题,坑了我整整两小时
前端·人工智能·后端
C澒5 小时前
AI 生码:A 类生码方案架构升级
前端·ai编程