onDestroy — phương thức cuối cùng của vòng đời Activity và Fragment trong Android, được gọi trước khi hủy hoàn toàn thành phần. onDestroy báo hiệu rằng Activity hoặc Fragment đang kết thúc công việc: tất cả tài nguyên phải được giải phóng, các fragment lồng nhau phải bị hủy, ViewModel phải được dọn dẹp. Theo Google, onDestroy được gọi trong 100% trường hợp kết thúc Activity, nhưng trong quá trình chết tiến trình (process death), hệ thống có thể bỏ qua hoàn toàn lệnh gọi onDestroy. Tài liệu Android về onDestroy nhấn mạnh rằng phương thức này không đảm bảo được gọi khi kết thúc bất thường.
Những Điểm Chính
onDestroy — một phương thức callback mà Android gọi trước khi hủy hoàn toàn Activity hoặc Fragment. Đây là cơ hội cuối cùng để nhà phát triển giải phóng tài nguyên, hủy các thao tác nền và hoàn tất công việc với dữ liệu. Sau khi onDestroy thực thi, phiên bản Activity/Fragment được đánh dấu để thu gom rác (GC) và không thể sử dụng được nữa.
Lý do gọi onDestroy:
Theo thống kê Google Android Vitals (2025), khoảng 12% tất cả các trường hợp hủy Activity xảy ra do xoay màn hình, 65% do finish() và 23% do thay đổi cấu hình. Tỷ lệ chết tiến trình bỏ qua onDestroy là khoảng 5–8% tùy thuộc vào thiết bị có RAM thấp (dưới 4 GB).
onDestroy được gọi trong hầu hết các kịch bản tiêu chuẩn, nhưng có những ngoại lệ quan trọng mà nhà phát triển phải xem xét. Hiểu về các đảm bảo của lệnh gọi onDestroy là rất quan trọng cho kiến trúc ứng dụng, đặc biệt là để lưu dữ liệu và hủy các tác vụ WorkManager.
Khi nào onDestroy được gọi:
Khi nào onDestroy KHÔNG được gọi:
Do thiếu đảm bảo về lệnh gọi onDestroy, Google khuyến nghị: không bao giờ dựa vào onDestroy để lưu dữ liệu quan trọng. Sử dụng onSaveInstanceState(), WorkManager hoặc Room với tính năng tự động lưu. onDestroy dùng để giải phóng tài nguyên, không phải để duy trì dữ liệu.
onDestroy tồn tại cho cả Activity và Fragment, nhưng với các hợp đồng khác nhau. Vòng đời của Fragment chi tiết hơn: ngoài onDestroy, còn có onDestroyView (hủy hệ thống phân cấp View) và onDetach (tách khỏi Activity).
| Thành phần | Phương thức hủy | Thứ tự | ViewModel tồn tại |
|---|---|---|---|
| Activity | onDestroy | onPause → onStop → onDestroy | Không (chỉ nếu ViewModelStore không được lưu) |
| Fragment | onDestroyView, onDestroy, onDetach | onPause → onStop → onDestroyView → onDestroy → onDetach | Có, nếu Fragment không bị xóa |
Sự khác biệt chính: View của Fragment được tạo lại thường xuyên hơn chính Fragment. Khi xoay màn hình, Fragment trải qua onDestroyView (hủy View), nhưng chính Fragment và ViewModel của nó vẫn tồn tại. onDestroyView là nơi thích hợp để dọn dẹp các tham chiếu View nhằm tránh rò rỉ bộ nhớ. onDestroy của Fragment tương tự như onDestroy của Activity, được gọi khi Fragment bị xóa hoàn toàn.
Các fragment con bị hủy trước onDestroy của Fragment cha. Trong Activity, các fragment con nhận onDestroy khi onDestroy của Activity cha được gọi. Thứ tự được đảm bảo: các fragment kết thúc trước Activity chứa chúng.
onDestroy nhằm giải phóng tất cả tài nguyên không nên tồn tại lâu hơn Activity hoặc Fragment. Không giống như onStop, giải phóng tài nguyên cho đến khi quay lại, onDestroy thực hiện dọn dẹp cuối cùng.
Danh sách kiểm tra các hành động bắt buộc trong onDestroy:
Những điều KHÔNG nên làm trong onDestroy: Đừng lưu dữ liệu trong onDestroy — sử dụng onPause hoặc onSaveInstanceState. Đừng bắt đầu Service hoặc tác vụ WorkManager mới — Activity sẽ bị hủy và bạn không thể theo dõi kết quả. Đừng cố gắng cập nhật giao diện người dùng — hệ thống phân cấp View đã bị hủy hoặc đang trong quá trình hủy; gọi findViewById() sẽ trả về null.
ViewModel được thiết kế để tồn tại sau onDestroy của Activity khi xoay màn hình, nhưng bị hủy cùng với Activity khi finish(). Hành vi bất đối xứng này là nguyên nhân chính gây nhầm lẫn cho các nhà phát triển.
Khi xoay màn hình:
Khi finish() (người dùng nhấn “Quay lại”):
Do đó, hủy viewModelScope trong onDestroy là không cần thiết — ViewModel sẽ tự thực hiện. Nếu bạn đang sử dụng lifecycleScope (gắn với Activity, không phải ViewModel), hãy hủy nó trong onDestroy qua lifecycleScope.cancel() hoặc quản lý Job thủ công.
Thể hiện quản lý lifecycleScope đúng cách trong Activity: một coroutine được khởi chạy để theo dõi trạng thái mạng và bị hủy trong onDestroy.
class NetworkMonitorActivity : AppCompatActivity() {
private val networkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) {
Log.d("NetworkMonitor", "Mạng khả dụng")
}
override fun onLost(network: Network) {
Log.d("NetworkMonitor", "Mạng bị mất")
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_network)
val connectivityManager = getSystemService(ConnectivityManager::class.java)
connectivityManager.registerDefaultNetworkCallback(networkCallback)
lifecycleScope.launch {
Log.d("NetworkMonitor", "Giám sát mạng đã bắt đầu")
}
}
override fun onDestroy() {
super.onDestroy()
val connectivityManager = getSystemService(ConnectivityManager::class.java)
connectivityManager.unregisterNetworkCallback(networkCallback)
Log.d("NetworkMonitor", "onDestroy: callback đã bị hủy")
}
}
Trong onDestroy, việc đăng ký callback mạng bị hủy. lifecycleScope tự động bị hủy khi vòng đời bị hủy — không cần hủy coroutine riêng biệt. Callback mạng phải được hủy đăng ký, nếu không nó sẽ tồn tại trong hệ thống ngay cả sau khi Activity bị hủy.
Một Fragment dọn dẹp tham chiếu View đúng cách trong onDestroyView, ngăn rò rỉ bộ nhớ do closures.
class ProfileFragment : Fragment() {
private var avatarView: ImageView? = null
private var progressBar: ProgressBar? = null
private val imageLoader = ImageLoader()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
avatarView = view.findViewById(R.id.avatar)
progressBar = view.findViewById(R.id.progress)
loadProfile()
}
private fun loadProfile() {
viewLifecycleOwner.lifecycleScope.launch {
try {
progressBar?.visibility = View.VISIBLE
val bitmap = imageLoader.load("https://example.com/avatar.png")
avatarView?.setImageBitmap(bitmap)
} finally {
progressBar?.visibility = View.GONE
}
}
}
override fun onDestroyView() {
super.onDestroyView()
avatarView = null
progressBar = null
imageLoader.cancel()
}
override fun onDestroy() {
super.onDestroy()
Log.d("ProfileFragment", "onDestroy: Fragment đã bị hủy hoàn toàn")
}
}
Trong onDestroyView, các tham chiếu View được đặt thành null — điều này ngăn rò rỉ bộ nhớ nếu một closure trong imageLoader giữ tham chiếu đến avatarView. Fragment và ViewModel của nó vẫn tồn tại cho đến onDestroy. imageLoader.cancel() hủy việc tải nếu Fragment rời khỏi màn hình.
Sử dụng isFinishing() cho phép phân biệt Activity kết thúc bằng lệnh người dùng hay để tạo lại.
class AnalyticsActivity : AppCompatActivity() {
private val analytics = Analytics()
override fun onDestroy() {
if (isFinishing) {
Log.d("AnalyticsActivity", "Activity kết thúc với finish() — đang gửi phân tích")
analytics.sendSessionEnd()
} else {
Log.d("AnalyticsActivity", "Activity đang được tạo lại (xoay/cấu hình) — không gửi phân tích")
}
super.onDestroy()
}
}
Kiểm tra isFinishing() là một mẫu quan trọng cho phân tích, ghi nhật ký và dọn dẹp dữ liệu phiên. Khi xoay, không nên gửi sự kiện kết thúc phiên — người dùng vẫn đang làm việc với ứng dụng. Theo Google Analytics, kiểm tra isFinishing() không đúng là nguyên nhân của 40% sự kiện phiên sai.
Câu Hỏi Thường Gặp
Có, có thể — trong quá trình chết tiến trình bởi hệ thống, Buộc dừng bởi người dùng hoặc kết thúc bất thường. Theo Google, khoảng 5–8% các kết thúc Activity xảy ra mà không có onDestroy được gọi. Nhà phát triển không nên dựa vào onDestroy để lưu dữ liệu quan trọng — sử dụng onPause hoặc onSaveInstanceState.
finish() — một lệnh gọi bắt đầu hủy Activity. onDestroy — một callback được gọi trong quá trình thực thi finish(). finish() là cần thiết để onDestroy được gọi khi kết thúc bình thường. finish() có thể được gọi bởi hệ thống hoặc nhà phát triển, onDestroy chỉ là callback hệ thống.
Có, nhất định phải cả trong Activity và Fragment. super.onDestroy() đảm bảo dọn dẹp đúng cách ChildFragmentManager, LoaderManager và các thành phần hệ thống khác. Bỏ qua super.onDestroy() dẫn đến rò rỉ bộ nhớ và lỗi khôi phục fragment.
onCleared() được gọi sau onDestroy của Activity hoặc Fragment, khi ViewModel không còn cần thiết. Khi xoay màn hình, onCleared() không được gọi — ViewModel tồn tại sau onDestroy. Thứ tự: onDestroy của Activity/Fragment → (ViewModelStore được dọn dẹp) → onCleared().
Kỹ thuật là có, nhưng không được khuyến nghị. Activity bị hủy ngay sau onDestroy và Service đã khởi động không còn được kiểm soát. Đối với tác vụ nền, hãy sử dụng WorkManager với độ trễ: WorkManager đảm bảo thực thi ngay cả sau khi Activity kết thúc và tồn tại qua chết tiến trình.
Tổng Kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.
Đọc thêm