Fragment Lifecycle: kiến thức cơ bản, các phương thức onCreateView và onViewCreated

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

Fragment Lifecycle là một chuỗi các phương thức callback được xác định chặt chẽ mà Android gọi trong suốt vòng đời của Fragment: từ khi tạo (onAttach) đến khi xóa hoàn toàn (onDetach). Fragment có vòng đời phức tạp hơn Activity — nó bao gồm 11 trạng thái và 7 callback chính. Fragment Lifecycle được quản lý thông qua FragmentManager và liên kết chặt chẽ với vòng đời của Activity chứa nó. Theo Google, Fragment được sử dụng trong 74% ứng dụng Android chạy trên API Level 21+, khiến việc hiểu Fragment Lifecycle trở nên bắt buộc đối với phát triển Android chuyên nghiệp. Tài liệu Android về Fragment Lifecycle mô tả tất cả các trạng thái và đảm bảo gọi.

Những điểm chính

  • Fragment Lifecycle bao gồm 11 callback: onAttach, onCreate, onCreateView, onViewCreated, onStart, onResume, onPause, onStop, onDestroyView, onDestroy, onDetach.
  • FragmentManager quản lý trạng thái của Fragment và đảm bảo thứ tự gọi chính xác trong các giao dịch.
  • onCreateView và onViewCreated là các phương thức chính để tạo và cấu hình UI của Fragment.
  • Fragment có thể tồn tại lâu hơn Activity của nó (khi xoay màn hình) và khôi phục trạng thái thông qua onSaveInstanceState.
  • viewLifecycleOwner — một Lifecycle riêng cho View của Fragment, bị hủy trong onDestroyView.

Fragment Lifecycle: kiến thức cơ bản về vòng đời

Fragment Lifecycle là một tập hợp các trạng thái và phương thức liên kết với nhau mà mỗi phiên bản Fragment trải qua từ khi tạo đến khi hủy. Không giống như Activity, vòng đời của Fragment gắn liền với hai ngữ cảnh: bản thân Fragment (tồn tại từ onAttach đến onDetach) và View của nó (tồn tại từ onCreateView đến onDestroyView). Sự tách biệt này là một tính năng chính của Fragment, cho phép nó sống sót qua việc hủy View khi xoay màn hình mà không hủy chính Fragment.

Trình tự đầy đủ các callback của Fragment:

  • onAttach(Context) — Fragment gắn kết với Activity. Được gọi đầu tiên. Context là Activity chủ.
  • onCreate(Bundle) — Fragment được khởi tạo. Tại đây ViewModel được tạo, các bộ điều hợp được cấu hình.
  • onCreateView(LayoutInflater, ViewGroup, Bundle) — hệ phân cấp View của Fragment được tạo. Trả về View gốc.
  • onViewCreated(View, Bundle) — View đã được tạo. Tại đây các phần tử UI được cấu hình, đăng ký LiveData được thiết lập.
  • onStart() — Fragment trở nên hiển thị. Hoạt ảnh bắt đầu, cảm biến được đăng ký.
  • onResume() — Fragment hoạt động, tương tác với người dùng.
  • onPause() — Fragment mất tiêu điểm. Hoạt ảnh dừng lại.
  • onStop() — Fragment không hiển thị. Tài nguyên không quan trọng được giải phóng.
  • onDestroyView() — hệ phân cấp View bị hủy. Các tham chiếu đến View được đặt thành null.
  • onDestroy() — Fragment bị hủy. Các coroutine không nằm trong viewModelScope bị hủy bỏ.
  • onDetach() — Fragment tách khỏi Activity. Dọn dẹp cuối cùng.

Theo Google, fragment trung bình trong một ứng dụng hiện đại trải qua vòng đời đầy đủ 3–5 lần mỗi phiên người dùng (do xoay màn hình và điều hướng). Xử lý chính xác tất cả các giai đoạn là nền tảng của sự ổn định UI.

Trạng thái của Fragment: từ INITIALIZED đến DESTROYED

FragmentManager quản lý Fragment thông qua năm trạng thái chính, được định nghĩa trong lớp Fragment.State. Mỗi trạng thái tương ứng với một tập hợp các callback cụ thể đã được thực thi.

Trạng tháiÝ nghĩaCallback đã thực thi
INITIALIZEDFragment đã được tạo, nhưng View chưa cóonAttach, onCreate
CREATEDView đã được tạo, nhưng Fragment không hiển thị+ onCreateView, onViewCreated
STARTEDFragment hiển thị, nhưng không hoạt động+ onStart
RESUMEDFragment hoạt động, tương tác với người dùng+ onResume
DESTROYEDFragment đã bị hủy+ onDestroyView, onDestroy, onDetach

FragmentManager di chuyển Fragment giữa các trạng thái dựa trên hành động của người dùng và sự kiện hệ thống. Khi thêm Fragment vào một bộ chứa, nó tuần tự đi qua INITIALIZED → CREATED → STARTED → RESUMED. Khi xóa — RESUMED → STARTED → CREATED → DESTROYED.

Trạng thái CREATED đặc biệt: View có thể bị hủy (sau onDestroyView), nhưng bản thân Fragment vẫn ở trạng thái CREATED (sau onDestroyView, trước onDestroy). Điều này cho phép FragmentManager giữ Fragment trong bộ nhớ mà không có View, cần thiết để sống sót qua xoay màn hình.

Sự khác biệt giữa Fragment Lifecycle và Activity Lifecycle

Fragment Lifecycle và Activity Lifecycle liên quan chặt chẽ nhưng có sự khác biệt cơ bản. Fragment luôn tồn tại bên trong Activity, và vòng đời của nó phụ thuộc vào Activity chủ, nhưng không giống hệt nó.

Khía cạnhActivityFragment
Số lượng callback7 (onCreate … onDestroy)11 (onAttach … onDetach)
Lifecycle riêng cho ViewKhôngCó (viewLifecycleOwner)
Sống sót qua xoay màn hìnhKhông (bị hủy)Có (ViewModel + Fragment sống sót)
Phụ thuộc vào chủKhôngPhụ thuộc vào Activity Lifecycle
Lưu trạng tháionSaveInstanceStateonSaveInstanceState (cấp Fragment)
Quản lýHệ thốngFragmentManager

Sự khác biệt thực tế chính: khi xoay màn hình, Activity bị hủy hoàn toàn (onDestroy) và được tạo lại (onCreate). Fragment khi xoay trải qua onDestroyView (View bị hủy) → onCreateView (View được tạo lại), nhưng bản thân Fragment và ViewModel của nó vẫn sống. Điều này làm cho Fragment trở thành bộ chứa lý tưởng cho logic UI cần sống sót qua các thay đổi cấu hình.

Thứ tự gọi khi xoay màn hình: Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView → (Activity bị hủy) → Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume.

FragmentManager: quản lý trạng thái và giao dịch

FragmentManager là lớp trung tâm chịu trách nhiệm thêm, xóa, thay thế các fragment và quản lý trạng thái của chúng. FragmentManager duy trì BackStack và đảm bảo thứ tự callback chính xác trong các giao dịch. Mỗi Activity và mỗi Fragment lồng nhau có FragmentManager riêng.

Các hoạt động chính của FragmentManager:

  • beginTransaction() — mở một giao dịch cho một nhóm hoạt động.
  • add() — thêm Fragment vào bộ chứa. Fragment trải qua toàn bộ vòng đời đến RESUMED.
  • replace() — thay thế Fragment hiện tại bằng Fragment mới. Tương đương remove() + add().
  • remove() — xóa Fragment. Fragment trải qua vòng đời từ RESUMED đến DESTROYED.
  • hide()/show() — ẩn/hiện Fragment mà không hủy View. Fragment chuyển sang STARTED khi ẩn, quay lại RESUMED khi hiện.
  • detach()/attach() — tách/gắn lại Fragment. detach hủy View (onDestroyView), attach tạo lại (onCreateView).
  • addToBackStack() — thêm giao dịch vào BackStack để điều hướng quay lại.

BackStack là ngăn xếp giao dịch của FragmentManager. Khi nhấn nút Quay lại của hệ thống, giao dịch cuối cùng trong BackStack được hoàn tác (popBackStack()). Fragment bị xóa qua popBackStack được khôi phục. Nếu BackStack trống, nhấn Quay lại sẽ kết thúc Activity.

Theo Google, 78% vấn đề với Fragment (trùng lặp, màn hình trống, IllegalStateException) liên quan đến việc sử dụng FragmentManager không đúng. Quy tắc chính: thực thi giao dịch qua commit() (bất đồng bộ) hoặc commitNow() (đồng bộ) tùy theo ngữ cảnh. commit() đảm bảo thứ tự chính xác khi có nhiều giao dịch.

Lưu trạng thái Fragment: onSaveInstanceState

Fragment hỗ trợ cơ chế lưu trạng thái riêng thông qua onSaveInstanceState, hoạt động độc lập với Activity. Fragment lưu trạng thái vào Bundle được truyền đến onCreate và onCreateView khi khôi phục.

Khi Fragment lưu trạng thái:

  • Khi xoay màn hình — View bị hủy, Fragment lưu trạng thái vào Bundle.
  • Khi Fragment được gắn lại vào Activity sau khi quy trình kết thúc.
  • Khi onSaveInstanceState được gọi từ Activity (hệ thống lan truyền việc lưu đến tất cả fragment con).

Cách tiếp cận hiện đại: sử dụng SavedStateHandle trong ViewModel để lưu trạng thái Fragment. SavedStateHandle tự động lưu và khôi phục dữ liệu khi xoay màn hình và quy trình kết thúc, không cần onSaveInstanceState thủ công. Google khuyến nghị SavedStateHandle là cách ưu tiên để lưu trạng thái UI trong Fragment.

setRetainInstance (không được dùng kể từ Fragment 1.3): trước đây Fragment có thể được giữ lại qua setRetainInstance(true) khi xoay màn hình. Cách tiếp cận này đã được thay thế bằng ViewModel + SavedStateHandle, hoạt động đáng tin cậy hơn và không yêu cầu cấu hình đặc biệt.

viewLifecycleOwner: vòng đời riêng của View

viewLifecycleOwner là một Lifecycle gắn với View của Fragment (từ onCreateView đến onDestroyView). Đây là một khái niệm quan trọng về cơ bản: các đăng ký LiveData/Flow được thực hiện qua viewLifecycleOwner tự động bị hủy khi View bị hủy (onDestroyView), nhưng không ảnh hưởng đến bản thân Fragment.

Sự khác biệt giữa viewLifecycleOwner và lifecycle của Fragment:

  • lifecycle (Fragment) — tồn tại từ onAttach đến onDetach. Các đăng ký vẫn hoạt động ngay cả sau khi View bị hủy.
  • viewLifecycleOwner — tồn tại từ onCreateView đến onDestroyView. Các đăng ký bị hủy khi View bị hủy.

Tại sao điều này quan trọng: nếu bạn đăng ký LiveData qua lifecycle của Fragment (this), sau onDestroyView đăng ký vẫn hoạt động và LiveData sẽ cố gắng cập nhật View null, gây ra NPE. Đăng ký qua viewLifecycleOwner đảm bảo rằng sau onDestroyView sẽ không có cập nhật UI nào xảy ra.

Quy tắc: trong Fragment, luôn sử dụng viewLifecycleOwner cho các đăng ký LiveData, Flow và coroutine liên quan đến UI. Đối với coroutine của ViewModel, sử dụng viewModelScope — nó gắn với ViewModel, không phải Fragment.

Ví dụ code Fragment bằng Kotlin

Ví dụ 1: Fragment cơ bản với onViewCreated và viewLifecycleOwner

Minh họa khởi tạo UI chính xác và đăng ký LiveData qua viewLifecycleOwner.

kotlin
class UserListFragment : Fragment() {
    private val viewModel: UserListViewModel by viewModels()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return inflater.inflate(R.layout.fragment_user_list, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val button: Button = view.findViewById(R.id.load_button)
        button.setOnClickListener { viewModel.loadUsers() }
        viewModel.users.observe(viewLifecycleOwner) { users ->
            Log.d("UserListFragment", "Đang cập nhật danh sách: ${users.size} người dùng")
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.d("UserListFragment", "onDestroyView: View đã bị hủy")
    }
}

Fragment nội suy bố cục trong onCreateView, cấu hình UI và đăng ký LiveData trong onViewCreated. Đăng ký qua viewLifecycleOwner là yêu cầu bắt buộc để ngăn rò rỉ bộ nhớ. onDestroyView ghi nhật ký hủy View — xác nhận rằng Fragment sống sót qua xoay màn hình.

Ví dụ 2: Fragment với FragmentManager và giao dịch

Minh họa thêm Fragment qua FragmentManager trong Activity, thay thế với BackStack và khôi phục.

kotlin
class HostActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_host)
        if (savedInstanceState == null) {
            supportFragmentManager.beginTransaction()
                .add(R.id.fragment_container, HomeFragment())
                .addToBackStack(null)
                .commit()
        }
    }

    fun openDetail(userId: String) {
        supportFragmentManager.beginTransaction()
            .replace(R.id.fragment_container, DetailFragment.newInstance(userId))
            .addToBackStack(null)
            .commit()
    }

    override fun onBackPressed() {
        if (supportFragmentManager.backStackEntryCount > 0) {
            supportFragmentManager.popBackStack()
        } else {
            super.onBackPressed()
        }
    }
}

class DetailFragment : Fragment() {
    companion object {
        fun newInstance(userId: String): DetailFragment {
            return DetailFragment().apply {
                arguments = Bundle().apply { putString("user_id", userId) }
            }
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val userId = arguments?.getString("user_id")
        Log.d("DetailFragment", "Đang tải chi tiết người dùng: $userId")
    }
}

Activity sử dụng supportFragmentManager để quản lý các fragment. Giao dịch add() với BackStack đảm bảo khi nhấn Quay lại, HomeFragment được khôi phục. openDetail() thay thế Fragment hiện tại bằng DetailFragment với tham số. Kiểm tra savedInstanceState == null ngăn trùng lặp fragment khi xoay màn hình.

Ví dụ 3: Fragment với LifecycleObserver và StateFlow

Sử dụng Flow và StateFlow trong Fragment với viewLifecycleOwner để cập nhật UI phản ứng.

kotlin
class SearchFragment : Fragment() {
    private val viewModel: SearchViewModel by viewModels()
    private var binding: FragmentSearchBinding? = null

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentSearchBinding.inflate(inflater, container, false)
        return binding!!.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding?.searchButton?.setOnClickListener {
            viewModel.search(binding?.queryInput?.text.toString())
        }
        viewLifecycleOwner.lifecycleScope.launch {
            viewModel.searchResults.collectLatest { results ->
                Log.d("SearchFragment", "Kết quả tìm kiếm: ${results.size}")
            }
        }
    }

    override fun onDestroyView() {
        super.onDestroyView()
        binding = null
    }
}

Fragment sử dụng View Binding để truy cập View. Coroutine viewLifecycleOwner.lifecycleScope.launch tự động bị hủy khi View bị hủy. Binding được đặt thành null trong onDestroyView để ngăn rò rỉ. StateFlow đảm bảo dữ liệu luôn mới khi View được tạo lại.

Các câu hỏi thường gặp

onViewCreated khác onCreateView như thế nào?

onCreateView tạo và trả về View gốc của Fragment. onViewCreated được gọi ngay sau khi View được tạo, đảm bảo View đã được khởi tạo hoàn toàn và sẵn sàng để cấu hình (findViewById, đăng ký). Google khuyến nghị chỉ nội suy bố cục trong onCreateView và thực hiện tất cả cấu hình UI trong onViewCreated.

Fragment thực sự bị hủy khi nào — ở onDestroy hay onDetach?

onDestroy — Fragment bị hủy như một đối tượng (ViewModel bị dọn dẹp, coroutine bị hủy bỏ). onDetach là callback cuối cùng, sau đó Fragment tách khỏi Activity. Thực tế tất cả tài nguyên nên được giải phóng trong onDestroyView (View) và onDestroy (Fragment). onDetach dùng để dọn dẹp tham chiếu đến Activity.

Tại sao Fragment biến mất sau khi xoay màn hình?

Fragment biến mất nếu nó không được thêm vào FragmentManager qua giao dịch có lưu trong BackStack hoặc nếu Activity không khôi phục FragmentManager trong onCreate. Giải pháp: thêm Fragment theo chương trình qua supportFragmentManager.beginTransaction().add() trong onCreate với kiểm tra savedInstanceState == null.

Fragment có thể tồn tại mà không có Activity không?

Không. Fragment luôn gắn với Activity thông qua FragmentManager. Ngay cả khi xoay màn hình, Activity được tạo lại và Fragment được gắn lại vào Activity mới. Tạo Fragment bên ngoài Activity là không thể — hàm tạo của Fragment yêu cầu hàm tạo rỗng để hệ thống khôi phục.

Fragments lồng nhau là gì và tại sao cần chúng?

Fragments lồng nhau (nested fragments) là Fragments bên trong một Fragment khác. Chúng được sử dụng để xây dựng các màn hình phức tạp: bảng tab, bảng có tab, master-detail. Fragments lồng nhau được quản lý bởi FragmentManager con (childFragmentManager). Google khuyến nghị không vượt quá 2 cấp độ lồng để tránh vấn đề hiệu suất.

Tổng kết

  • Fragment Lifecycle bao gồm 11 callback: onAttach → onCreate → onCreateView → onViewCreated → onStart → onResume → onPause → onStop → onDestroyView → onDestroy → onDetach.
  • FragmentManager quản lý trạng thái của Fragment (INITIALIZED → CREATED → STARTED → RESUMED → DESTROYED) và BackStack giao dịch.
  • Fragment sống sót qua xoay màn hình — View bị hủy (onDestroyView), nhưng Fragment và ViewModel vẫn sống.
  • viewLifecycleOwner là Lifecycle riêng cho View của Fragment; bắt buộc cho đăng ký LiveData và coroutine UI.
  • Lưu trạng thái Fragment — qua onSaveInstanceState hoặc SavedStateHandle trong ViewModel.
  • Giao dịch Fragment được thực thi qua FragmentManager với commit() (bất đồng bộ) hoặc commitNow() (đồng bộ).
  • Luôn đặt binding và tham chiếu View thành null trong onDestroyView để ngăn rò rỉ bộ nhớ.

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