别再执着于 `onDestroy()`:从“何时执行”到“不应依赖”的思维转变

280 阅读4分钟

一句话总结:

纠结于 finish() 后 onDestroy() 的执行时机是一个误区。真相是:系统根本不保证 onDestroy() 会被调用。健壮的应用依赖的是对称的生命周期管理(如 onStart/onStop),以及 Jetpack Lifecycle 带来的架构革新。


一、常见误解:为什么 finish()onDestroy() 会“延迟”?

你观察到的现象是正确的。当你调用 finish() 后,onDestroy() 并不会同步执行。其原因主要有二:

  1. 主线程消息队列: 所有生命周期回调(包括 onPause, onStop, onDestroy)都是通过主线程的 Looper 派发的 Message。如果 finish() 之后主线程有其他耗时任务,onDestroy 消息就必须排队等待。
  2. 转场动画: Activity 的退出动画会持有窗口,直到动画播放完毕,销毁流程才得以继续。

(⚠️警告:以下代码仅为演示,在真实项目中会导致 ANR 崩溃!)

Log.d("Lifecycle", "调用 finish()")
finish()
// 恶意阻塞主线程,onDestroy 将在 sleep 结束后才被调用
Thread.sleep(5000) 

虽然我们解释了延迟的原因,但这引出了一个更深刻、更致命的问题。


二、残酷的真相:onDestroy() 是一个“礼貌性”的通知,而非“契约性”的承诺

我们不应关心 onDestroy() 延迟多久,而应该清醒地认识到:Android 系统不提供任何保证 onDestroy() 会被执行。

最常见的情况是**“进程终止”(Process Death)**:

当你的应用进入后台,如果系统内存不足,系统会为了回收内存而直接杀死你的应用进程。这个过程是内核级别的,不会给予你的应用任何执行最后代码的机会。此时,别说 onDestroy(),任何清理代码都不会被执行。你的应用进程会像断电一样瞬间消失。

结论:任何关键的资源释放、数据保存等逻辑,如果仅仅依赖 onDestroy() 来执行,那么它注定是不可靠的。


三、正确的资源管理之道:对称的生命周期配对

既然 onDestroy() 不可靠,我们应该在哪里释放资源?答案是:在哪里获取,就在其对称的生命周期回调中释放。

获取资源的回调释放资源的回调典型场景
onCreateonDestroy非关键的、与整个 Activity/进程生命周期绑定的资源清理。
onStartonStop(最常用) 注册/反注册监听器 (BroadcastReceiver, 事件总线),启动/停止动画。
onResumeonPause获取/释放独占性资源,如相机、麦克风。

黄金法则: 对于绝大部分需要在 UI 可见时工作、不可见时停止的资源,onStart()/onStop() 是最安全、最可靠的管理区间。


四、现代化的终极方案:拥抱 Jetpack Lifecycle

手动在 onStart/onStop 中管理大量资源依然繁琐且容易出错。现代 Android 开发通过 Jetpack Lifecycle 库,将这一过程变得自动化和解耦。

核心思想: 让你的业务组件(如一个 GPS 监听器)自己感知生命周期,自我管理,而不是让 Activity/Fragment 成为一个事必躬亲的“保姆”。

示例:一个自管理的 GPS 监听器

// 1. 创建一个生命周期观察者
class MyGpsListener(
    private val context: Context,
    private val lifecycle: Lifecycle, // 传入生命周期
    private val callback: (Location) -> Unit
) : DefaultLifecycleObserver { // 实现 DefaultLifecycleObserver

    private var fusedLocationClient: FusedLocationProviderClient? = null

    init {
        // 将自己添加到生命周期观察者列表
        lifecycle.addObserver(this)
    }

    override fun onStart(owner: LifecycleOwner) {
        // 当生命周期进入 onStart 时,自动开始监听
        Log.d("GPS", "开始监听位置...")
        fusedLocationClient = LocationServices.getFusedLocationProviderClient(context)
        // ... 启动位置更新 ...
    }

    override fun onStop(owner: LifecycleOwner) {
        // 当生命周期进入 onStop 时,自动停止监听
        Log.d("GPS", "停止监听位置。")
        fusedLocationClient?.removeLocationUpdates(...)
        fusedLocationClient = null
    }

    fun destroy() {
        // 提供一个方法将自己从观察者中移除
        lifecycle.removeObserver(this)
    }
}

Activity 中的代码变得极其简洁:

class MyActivity : AppCompatActivity() {
    private lateinit var gpsListener: MyGpsListener

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        // 只需创建实例,它就会自我管理
        gpsListener = MyGspListener(this, lifecycle) { location ->
            // 更新 UI
        }
    }
}

结论: 现代 Android 架构的核心,是从面向回调的命令式编程,转向面向生命周期和状态的声明式编程

旧思维(面向回调)新思维(面向生命周期架构)
核心onDestroy 里我该做什么?
实现onStart/onStop/onDestroy 中手动写满 register/unregister 代码
健壮性低,容易遗忘清理,且无法应对进程终止