Fragment Lifecycle — توالی کاملاً مشخصی از متدهای بازخورد (callback) است که Android در طول عمر Fragment فراخوانی میکند: از ایجاد (onAttach) تا حذف کامل (onDetach). Fragment چرخه حیات پیچیدهتری نسبت به Activity دارد — شامل 11 حالت و 7 بازخورد اصلی است. Fragment Lifecycle از طریق FragmentManager مدیریت میشود و با چرخه حیات Activity میزبان خود ارتباط تنگاتنگی دارد. طبق دادههای Google، Fragment در 74% اپلیکیشنهای Android که در API Level 21+ کار میکنند استفاده میشود که درک Fragment Lifecycle را برای توسعهحرفهای Android ضروری میسازد. مستندات Android درباره Fragment Lifecycle تمام حالتها و تضمینهای فراخوانی را شرح میدهد.
نکات مهم
Fragment Lifecycle مجموعهای از حالتها و متدهای به هم پیوسته است که هر نمونه Fragment از لحظه ایجاد تا نابودی از آن عبور میکند. برخلاف Activity، چرخه حیات Fragment به دو زمینه متصل است: خود Fragment (از onAttach تا onDetach زندگی میکند) و View آن (از onCreateView تا onDestroyView زندگی میکند). این تفکیک ویژگی کلیدی Fragment است که به آن اجازه میدهد نابودی View را در چرخش صفحه بدون نابودی خود Fragment تحمل کند.
توالی کامل بازخوردهای Fragment:
به گفته Google، فرگمنت متوسط در یک اپلیکیشن مدرن 3–5 بار در هر جلسه کاربر چرخه کامل را طی میکند (به دلیل چرخش صفحه و ناوبری). پردازش صحیح همه فازها اساس پایداری UI است.
FragmentManager Fragment را از طریق پنج حالت اصلی مدیریت میکند که در کلاس Fragment.State تعریف شدهاند. هر حالت با مجموعه مشخصی از بازخوردهای اجرا شده مطابقت دارد.
| حالت | معنی | بازخوردهای اجرا شده |
|---|---|---|
| INITIALIZED | Fragment ایجاد شده، اما View هنوز وجود ندارد | onAttach, onCreate |
| CREATED | View ایجاد شده، اما Fragment قابل مشاهده نیست | + onCreateView, onViewCreated |
| STARTED | Fragment قابل مشاهده، اما غیرفعال است | + onStart |
| RESUMED | Fragment فعال است، با کاربر تعامل دارد | + onResume |
| DESTROYED | Fragment نابود شده | + onDestroyView, onDestroy, onDetach |
FragmentManager Fragment را بسته به اقدامات کاربر و رویدادهای سیستم بین حالتها جابهجا میکند. هنگام افزودن Fragment به کانتینر، به ترتیب INITIALIZED → CREATED → STARTED → RESUMED را طی میکند. هنگام حذف — RESUMED → STARTED → CREATED → DESTROYED.
حالت CREATED — ویژه است: View ممکن است نابود شود (بعد از onDestroyView)، اما خود Fragment در حالت CREATED باقی میماند (بعد از onDestroyView، قبل از onDestroy). این به FragmentManager اجازه میدهد Fragment را بدون View در حافظه نگه دارد که برای تحمل چرخش صفحه ضروری است.
Fragment Lifecycle و Activity Lifecycle ارتباط نزدیکی دارند اما تفاوتهای اساسی دارند. Fragment همیشه درون Activity زندگی میکند و چرخه حیات آن به Activity میزبان وابسته است اما با آن یکسان نیست.
| جنبه | Activity | Fragment |
|---|---|---|
| تعداد بازخوردها | 7 (onCreate … onDestroy) | 11 (onAttach … onDetach) |
| Lifecycle جداگانه برای View | خیر | بله (viewLifecycleOwner) |
| تحمل چرخش | خیر (نابود میشود) | بله (ViewModel + Fragment زنده میمانند) |
| وابستگی به میزبان | خیر | وابسته به Activity Lifecycle |
| ذخیره وضعیت | onSaveInstanceState | onSaveInstanceState (Fragment) |
| مدیریت | سیستم | FragmentManager |
تفاوت عملی اصلی: هنگام چرخش صفحه، Activity کاملاً نابود میشود (onDestroy) و دوباره ایجاد میشود (onCreate). Fragment هنگام چرخش از onDestroyView (View نابود میشود) → onCreateView (View دوباره ایجاد میشود) عبور میکند، اما خود Fragment و ViewModel آن زنده میمانند. این باعث میشود Fragment کانتینری ایدهآل برای منطق UI باشد که باید تغییرات پیکربندی را تحمل کند.
ترتیب فراخوانیها هنگام چرخش صفحه: Activity.onPause → Fragment.onPause → Activity.onStop → Fragment.onStop → Activity.onDestroy → Fragment.onDestroyView → (Activity نابود شد) → Activity.onCreate → Fragment.onAttach → Fragment.onCreate → Fragment.onCreateView → Fragment.onViewCreated → Activity.onStart → Fragment.onStart → Activity.onResume → Fragment.onResume.
FragmentManager — کلاس مرکزی مسئول افزودن، حذف، جایگزینی فرگمنتها و مدیریت حالتهای آنها. FragmentManager پشته BackStack را نگهداری میکند و ترتیب صحیح بازخوردها را در تراکنشها تضمین میکند. هر Activity و هر Fragment تو در تو FragmentManager مخصوص خود را دارند.
عملیات اصلی FragmentManager:
BackStack — پشته تراکنشهای FragmentManager. با فشردن دکمه سیستم «بازگشت»، آخرین تراکنش در BackStack برگردانده میشود (popBackStack()). Fragment حذف شده از طریق popBackStack بازیابی میشود. اگر BackStack خالی باشد، فشردن «بازگشت» Activity را پایان میدهد.
به گفته Google، 78% مشکلات با Fragment (تکرار، صفحههای خالی، IllegalStateException) به استفاده نادرست از FragmentManager مربوط میشود. قانون اصلی: تراکنشها را از طریق commit() (ناهمزمان) یا commitNow() (همزمان) بسته به زمینه اجرا کنید. commit() ترتیب صحیح را در تراکنشهای متعدد تضمین میکند.
Fragment مکانیزم ذخیره وضعیت خود را از طریق onSaveInstanceState پشتیبانی میکند که مستقل از Activity کار میکند. Fragment وضعیت را در Bundle ذخیره میکند که هنگام بازیابی به onCreate و onCreateView منتقل میشود.
چه زمانی Fragment وضعیت را ذخیره میکند:
رویکرد مدرن: از SavedStateHandle در ViewModel برای ذخیره وضعیت Fragment استفاده کنید. SavedStateHandle به طور خودکار دادهها را در چرخش صفحه و process death ذخیره و بازیابی میکند، بدون نیاز به onSaveInstanceState دستی. Google SavedStateHandle را به عنوان روش ترجیحی برای ذخیره وضعیت UI در Fragment توصیه میکند.
setRetainInstance (از Fragment 1.3 منسوخ شده): قبلاً Fragment میتوانست از طریق setRetainInstance(true) در چرخش صفحه حفظ شود. این رویکرد با ViewModel + SavedStateHandle جایگزین شده است که قابلاعتمادتر کار میکنند و نیاز به پیکربندی خاصی ندارند.
viewLifecycleOwner — Lifecycle متصل به View Fragment (از onCreateView تا onDestroyView). این مفهوم اساساً مهم است: اشتراکهای LiveData/Flow که از طریق viewLifecycleOwner انجام میشوند با نابودی View (onDestroyView) به طور خودکار لغو میشوند، اما روی خود Fragment تأثیر نمیگذارند.
تفاوت viewLifecycleOwner با lifecycle Fragment:
چرا این مهم است: اگر از طریق lifecycle Fragment (this) در LiveData مشترک شوید، پس از onDestroyView اشتراک فعال میماند و LiveData سعی میکند View null را بهروزرسانی کند و باعث NPE میشود. اشتراک از طریق viewLifecycleOwner تضمین میکند که پس از onDestroyView هیچ بهروزرسانی UI رخ ندهد.
قانون: در Fragment همیشه از viewLifecycleOwner برای اشتراکهای LiveData، Flow و کوروتینهای مرتبط با UI استفاده کنید. برای کوروتینهای ViewModel از viewModelScope استفاده کنید — این به ViewModel متصل است، نه به Fragment.
مقداردهی اولیه صحیح UI و اشتراک LiveData از طریق viewLifecycleOwner را نشان میدهد.
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", "بهروزرسانی لیست: ${users.size} کاربر")
}
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("UserListFragment", "onDestroyView: View نابود شد")
}
}
Fragment layout را در onCreateView باد میکند، UI را پیکربندی میکند و در onViewCreated در LiveData مشترک میشود. اشتراک از طریق viewLifecycleOwner — یک الزام اجباری برای جلوگیری از نشت حافظه است. onDestroyView نابودی View را ثبت میکند — تأییدی که Fragment چرخش صفحه را تحمل میکند.
افزودن Fragment از طریق FragmentManager در Activity، جایگزینی با BackStack و بازیابی را نشان میدهد.
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", "بارگذاری جزئیات کاربر: $userId")
}
}
Activity از supportFragmentManager برای مدیریت فرگمنتها استفاده میکند. تراکنش add() با BackStack تضمین میکند که با فشردن «بازگشت» HomeFragment بازیابی شود. openDetail() Fragment فعلی را با DetailFragment با آرگومانها جایگزین میکند. بررسی savedInstanceState == null از تکرار فرگمنتها در چرخش صفحه جلوگیری میکند.
استفاده از Flow و StateFlow در Fragment با viewLifecycleOwner برای بهروزرسانی واکنشگرای UI.
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", "نتایج جستجو: ${results.size}")
}
}
}
override fun onDestroyView() {
super.onDestroyView()
binding = null
}
}
Fragment از View Binding برای دسترسی به View استفاده میکند. کوروتین viewLifecycleOwner.lifecycleScope.launch با نابودی View به طور خودکار لغو میشود. Binding برای جلوگیری از نشت در onDestroyView صفر میشود. StateFlow بهروزرسانی دادهها را هنگام بازسازی View تضمین میکند.
سوالات متداول
onCreateView — View ریشه Fragment را ایجاد و برمیگرداند. onViewCreated — بلافاصله پس از ایجاد View فراخوانی میشود و تضمین میکند View کاملاً مقداردهی اولیه شده و برای پیکربندی آماده است (findViewById، اشتراکها). Google توصیه میکند در onCreateView فقط layout را باد کنید و تمام پیکربندی UI را در onViewCreated انجام دهید.
onDestroy — Fragment به عنوان یک شی نابود شده است (ViewModel پاک میشود، کوروتینها لغو میشوند). onDetach — آخرین بازخورد است که پس از آن Fragment از Activity جدا میشود. عملاً تمام منابع باید در onDestroyView (View) و onDestroy (Fragment) آزاد شوند. onDetach برای پاکسازی ارجاعات به Activity است.
Fragment ناپدید میشود اگر از طریق تراکنش با ذخیره در BackStack به FragmentManager اضافه نشده باشد یا اگر Activity FragmentManager را در onCreate بازیابی نکند. راهحل: Fragment را به صورت برنامهنویسی از طریق supportFragmentManager.beginTransaction().add() در onCreate با بررسی savedInstanceState == null اضافه کنید.
خیر. Fragment همیشه از طریق FragmentManager به Activity متصل است. حتی در چرخش صفحه Activity بازسازی میشود و Fragment دوباره به Activity جدید متصل میشود. ایجاد Fragment خارج از Activity غیرممکن است — سازنده Fragment برای بازیابی توسط سیستم به سازنده خالی نیاز دارد.
Nested fragments (فرگمنتهای تو در تو) — Fragment درون Fragment دیگر. برای ساخت صفحههای پیچیده استفاده میشود: پنلهای تب، پنلهای زبانهای، master-detail. فرگمنتهای تو در تو توسط FragmentManager فرزند (childFragmentManager) مدیریت میشوند. Google توصیه میکند برای جلوگیری از مشکلات عملکرد، از 2 سطح تو در تو تجاوز نکنید.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید