許多 Android 開發者為了實現「一鍵退出應用」,嘗試在 Application 類別中呼叫 System.exit(0)。然而這違背了 Android 設計哲學,會導致嚴重的副作用:Activity 生命週期中斷、資源洩漏、重啟閃退等。本文深度剖析問題根源,提供符合規範的退出方案。
onDestroy()、onStop() 等生命週期方法不被呼叫,資源(資料庫連線、執行緒、檔案控制代碼)無法正常釋放。您透過 ActivityLifecycleCallbacks 計數 Activity 數量,歸零時呼叫 System.exit(0)。問題在於:
onActivityDestroyed 執行時,Activity 視圖可能尚未完全拆解,強制退出會中斷清理流程。Thread.sleep(300) 模擬延遲反而加劇競態條件:主執行緒阻塞時,系統可能已啟動新 Activity,導致新舊進程狀態衝突。onSaveInstanceState 後的進程被殺重建場景,計數器會歸零錯誤觸發退出。將「退出」定義為「清除任務堆疊並回到桌面」,而非殺進程:
fun exitApp(context: Context) {
val intent = Intent(context, MainActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
putExtra("EXIT_APP", true)
}
context.startActivity(intent)
// 可選:若需確保當前 Activity 結束
(context as? Activity)?.finish()
}
// 在 MainActivity.onCreate 中處理
if (intent.getBooleanExtra("EXIT_APP", false)) {
finish() // 結束最後一個 Activity,任務堆疊為空,用戶回到桌面
}
private var lastBackPressTime = 0L
override fun onBackPressed() {
if (System.currentTimeMillis() - lastBackPressTime < 2000) {
super.onBackPressed() // 正常結束 Activity
} else {
Toast.makeText(this, "再按一次退出", Toast.LENGTH_SHORT).show()
lastBackPressTime = System.currentTimeMillis()
}
}
若確有資源需主動釋放(如 Service、Socket、執行緒池),在 Application 或基底 Activity 中統一管理:
class App : Application() {
private val resources = mutableListOf()
fun registerResource(res: AutoCloseable) = resources.add(res)
override fun onTerminate() { // 僅模擬器/測試環境會調用
resources.forEach { it.close() }
super.onTerminate()
}
}
// 在需要清理的組件中
(App.instance).registerResource(database)
(App.instance).registerResource(threadPool)
理解以下機制有助於設計正確退出邏輯:
不要使用 System.exit(0)。Android 應用退出應遵循系統設計:清空任務堆疊、正常結束 Activity、讓系統管理進程生命週期。您的閃退問題根源在於強制終止破壞了進程狀態一致性。採用 FLAG_ACTIVITY_CLEAR_TASK 模式,既能滿足「一鍵退出」業務需求,又能保證穩定性與規範性。