onDestroy: nó là gì, kết thúc công việc của Activity trong Android

Tác giả: IT Sectr Đã đăng: 2026-03-04 Thời gian đọc: 8 phút

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 — lệnh gọi cuối cùng trước khi hủy Activity hoặc Fragment, nhằm dọn dẹp tài nguyên cuối cùng.
  • >Lệnh gọi onDestroy không được đảm bảo trong quá trình chết tiến trình bởi hệ thống — đừng dựa vào nó để lưu dữ liệu quan trọng.
  • Trong onDestroy, bạn phải hủy các tác vụ nền, đóng socket và cơ sở dữ liệu, dọn dẹp ViewModelStore.
  • Khác biệt với onStop: onStop — mất khả năng hiển thị (Activity còn trong bộ nhớ), onDestroy — hủy hoàn toàn.
  • isFinishing() trong onDestroy cho biết Activity kết thúc bằng lệnh người dùng (finish()) hay theo quyết định của hệ thống.
  • onDestroy: nó là gì trong Android?

    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:

    • Gọi finish() rõ ràng — người dùng nhấn “Quay lại” hoặc nhà phát triển gọi finishActivity().
    • Xoay màn hình — Activity bị hủy và được tạo lại với cấu hình mới.
    • Thay đổi cấu hình — bàn phím, thay đổi ngôn ngữ, thay đổi kích thước màn hình (đa cửa sổ).
    • Quyết định của hệ thống — Android hủy Activity để giải phóng tài nguyên (nhưng onDestroy có thể không được gọi).

    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).

    Khi nào onDestroy được gọi — và khi nào không được gọi

    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:

    • Người dùng nhấn nút “Quay lại” — Activity.finish() → onPause → onStop → onDestroy.
    • Xoay màn hình — Activity bị hủy (onPause → onStop → onDestroy), sau đó được tạo lại.
    • Thay đổi cấu hình — cài đặt hệ thống yêu cầu tạo lại Activity.
    • Gọi finishAffinity() — kết thúc tất cả Activities trong ngăn xếp.
    • Xóa Fragment khỏi FragmentManager — Fragment nhận: onPause → onStop → onDestroyView → onDestroy → onDetach.

    Khi nào onDestroy KHÔNG được gọi:

    • Chết tiến trình bởi hệ thống — Android hủy toàn bộ tiến trình ứng dụng khi thiếu bộ nhớ. Activity không nhận được onDestroy vì tiến trình kết thúc ở cấp nhân Linux.
    • Kết thúc bất thường — một ngoại lệ không được bắt trong luồng chính hủy ứng dụng mà không gọi onDestroy.
    • Buộc dừng — người dùng buộc dừng ứng dụng trong cài đặt.

    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 trong Activity và Fragment: điểm chung và khác biệt

    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ầnPhương thức hủyThứ tựViewModel tồn tại
    ActivityonDestroyonPause → onStop → onDestroyKhông (chỉ nếu ViewModelStore không được lưu)
    FragmentonDestroyView, onDestroy, onDetachonPause → onStop → onDestroyView → onDestroy → onDetachCó, 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.

    Làm gì trong onDestroy: danh sách kiểm tra dọn dẹp

    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:

    • Hủy coroutine và Flow — hủy các job không gắn với viewModelScope. viewModelScope tự động bị hủy, nhưng lifecycleScope gắn với vòng đời Activity.
    • Đóng socket và kênh — WebSocket (OkHttp), BluetoothSocket, ServerSocket. Giữ chúng mở sau khi hủy là rò rỉ tài nguyên hệ thống.
    • Đóng tệp và luồng — FileInputStream, FileOutputStream, Cursor. Cursor có thể gây ANR trên ContentProvider nếu không được đóng.
    • Hủy đăng ký ContentObserver — nếu Activity đang theo dõi thay đổi nội dung (danh bạ, thư viện phương tiện).
    • Hủy đăng ký BroadcastReceiver — các receiver đã đăng ký động phải bị hủy.
    • Đóng cơ sở dữ liệu — Room tự động đóng kết nối khi Application bị hủy, nhưng SQLiteDatabase trực tiếp yêu cầu close() thủ công.

    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.

    onDestroy và ViewModel: làm việc cùng nhau

    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:

    • Activity: onPause → onStop → onDestroy (Activity bị hủy).
    • ViewModel: KHÔNG bị hủy — ViewModelStore được lưu và chuyển đến Activity mới.
    • Activity mới: onCreate → onStart → onResume, nhận cùng ViewModel.

    Khi finish() (người dùng nhấn “Quay lại”):

    • Activity: onPause → onStop → onDestroy.
    • ViewModel: onCleared() — được gọi sau onDestroy của Activity.
    • Tất cả coroutine của viewModelScope tự động bị hủy.

    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.

    Ví dụ mã với onDestroy bằng Kotlin

    Ví dụ 1: onDestroy Activity với hủy coroutine lifecycleScope

    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.

    kotlin
    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.

    Ví dụ 2: onDestroy Fragment với dọn dẹp tham chiếu View

    Một Fragment dọn dẹp tham chiếu View đúng cách trong onDestroyView, ngăn rò rỉ bộ nhớ do closures.

    kotlin
    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.

    Ví dụ 3: Kiểm tra isFinishing trong onDestroy

    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.

    kotlin
    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

    onDestroy có thể không được gọi không?

    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.

    Sự khác biệt giữa onDestroy và finish() là gì?

    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.

    Tôi có cần gọi super.onDestroy() trong Fragment khô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.

    Khi nào onCleared() được gọi trong ViewModel so với onDestroy?

    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().

    Tôi có thể khởi động Service từ onDestroy không?

    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

    • onDestroy — callback vòng đời cuối cùng của Activity và Fragment, được gọi trước khi hủy hoàn toàn thành phần.
    • Lệnh gọi onDestroy không được đảm bảo khi chết tiến trình — khoảng 5–8% kết thúc xảy ra mà không có nó.
    • Trong onDestroy, cần giải phóng: callback mạng, socket, luồng tệp, BroadcastReceiver, ContentObserver.
    • ViewModel.onCleared() được gọi sau onDestroy của Activity — viewModelScope tự động bị hủy.
    • onDestroyView trong Fragment (tách biệt với onDestroy) — nơi thích hợp để đặt tham chiếu View thành null.
    • Kiểm tra isFinishing() trong onDestroy cho phép phân biệt kết thúc finish() với tạo lại do thay đổi cấu hình.
    • Đừng dựa vào onDestroy để lưu dữ liệu — sử dụng onPause hoặc onSaveInstanceState.

    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.

    Thảo luận dự án

    Đọc thêm