# 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 监听器未反注册

场景 :BroadcastReceiver、EventBus、LiveData、自定义监听器等未在 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

场景 :在 lifecycleScope 或 viewModelScope 外启动协程,且持有 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()
}

避免 :用 lifecycleScope、viewModelScope 等,避免 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 资源未关闭

场景 :Cursor、InputStream、OutputStream、Socket、OkHttp Response.body 等未关闭。

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

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

避免 :用 use 或 try-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 定时器未取消

场景 :Timer、Handler.postDelayed、ScheduledExecutorService 等在 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
结构化并发 使用 lifecycleScope、viewModelScope 等,避免 GlobalScope
资源 使用 use 或 try-finally 确保资源关闭

三、检查清单

检查项 说明
单例/静态变量 是否持有 Activity?改用 WeakReference 或 applicationContext
Handler / Runnable 是否在 onDestroy 中 removeCallbacksAndMessages?
监听器 BroadcastReceiver、EventBus 等是否在 onDestroy 中 unregister?
协程 是否用 lifecycleScope 而非 GlobalScope?
网络请求 是否在 onDestroy 中 cancel?
资源 Cursor、Stream、Response.body 是否关闭?
相关推荐
颜进强12 分钟前
18 · NestJS动态模块与 forRoot:`imports: [ConfigModule.forRoot({...})]` 到底在 import 什么
前端·后端·ai编程
YZ12255224 分钟前
【Docker专题】使用Docker部署Vue-Flask项目前后端分离版【前端Docker部署】
前端·vue.js·docker
liangshanbo121526 分钟前
浏览器渲染过程:高级前端面试题
前端
太子釢29 分钟前
React 函数组件与 Hook 实践指南
前端·react.js
颜进强37 分钟前
17 · 自定义 Provider 四形态:useValue / useClass / useFactory / useExisting 到底在选什么
前端·后端·ai编程
传人once1 小时前
网页文字滚动效果如何做
前端·css·html·jquery·html5
liangshanbo12151 小时前
面试题:React 中 Lane 和 Scheduler 是怎么配合的?
前端·react.js·前端框架
liangshanbo12151 小时前
请求拦截器相关面试题
java·前端·javascript
ShawnLiaoking1 小时前
ADS305 Big Data Lecuture4 Web Scraping
大数据·前端
liangshanbo12151 小时前
React面试题:Hooks 为什么不能在分支和循环里面写?
前端·javascript·react.js