چرخه ی حیات فرگمنت(Fragment Lifecycle) اندروید برای توسعه دهندگان حرفه ای

سه شنبه ۰۵ تیر ۹۷ توسط فاطمه بهاروند

اگر شما از من بخواهید سه مورد از پیچیده ترین جنبه های چارچوب اندروید را برای شما لیست کنم، قطعا چرخه ی حیات فرگمنت یکی از آنان خواهد بود. این یک تعارف نیست.

فرگمنت ها در سال 2010 توسط Dianne که در میان توسعه دهندگان اندروید شناخته شده است به اندروید معرفی شد. این متن توسط Dianne نوشته شده است:

نویسنده: Dianne Hackborn

تاریخ: پنجشنبه 15 آپریل 14:45:25 2010 -0700

معرفی فرگمنت.

اجرای اساس یک API برای سازمان دهی یک اکتیویتی به صورت قطعات جدا و گسسته است. در حال حاضر اضافه نمودن و حذف فرگمنت ها، همچنین پیاده سازی callbackهای اساسی چرخه ی حیات بر روی فرگمنت ها قابل انجام است.

ایده ی "سازمان دهی یک اکتیویتی به صورت قطعات جدا و گسسته" بسیار عالی بود. اگرچه امروزه وضعیت فرگمنت ها از لحاظ پیاده سازی اولیه و سیر تکامل این API بسیار تاسف بار است.

تاکنون فرگمنت ها از جمله بحث برانگیزترین APIهای اندروید به شمار می آیند. بسیاری از توسعه دهندگان حرفه ای اندروید تحت هیچ شرایطی از فرگمنت در پروژه های خود استفاده نخواهند کرد، این امر به دلیل عدم اطلاع از فواید آن نیست بلکه به دلیل مشاهده ی ضعف ها و محدودیت های آن است. این توسعه دهندگان یک هدف دارند.

ساده سازی پیچیدگی چرخه ی حیات فرگمنت حقیقتا اغراق آمیز است. نگاهی به این نمودار بیندازید، ترسناک است. خوشبختانه شما برای استفاده از فرگمنت در برنامه ی خود نیازی به یادگیری زیر و بم این چرخه ی حیات ندارید.

در این مقاله من رویکرد خود را برای مدیریت چرخه ی حیات فرگمنت و چشم پوشی از بیشتر این پیچیدگی ها را برای شما شرح می دهم. من از این روش سال هاست که استفاده می کنم و واقعا کارگشاست.

چرخه ی حیات اکتیویتی

همانطور که اندکی بعد خواهید دید من زمانی که از فرگمنت استفاده می کنم در تلاشم آن را تا حد امکان از اکتیویتی جدا کنم، با این حال این موضوع حقیقت شباهت بسیار بین آنان را تغییر نمی دهد.

من پیش از این مقاله ای نوشته ام که رویکرد من به مدیریت چرخه ی حیات اکتیویتی را توصیف می کند. آن مقاله بازخورد بسیار مثبتی دریافت کرد و در کمتر از یک ماه به لیست محبوبترین مقالات وبلاگ من اضافه شد.

همانطور که گفتم چرخه ی حیات اکتیویتی و فرگمنت در بسیاری از موارد مشابه یکدیگرند. بنابراین در این مقاله من بسیاری از شباهت های بین آن ها را شرح می دهم، با این حال نمی خواهم دوباره کاری شود به همین دلیل فرض می کنم شما مقاله ی مربوط به اکتیویتی ها را مطالعه نموده اید اگر اینچنین نیست پیش از آنکه مطالعه ی این مقاله را ادامه دهید آن را مطالعه نمایید.

نگاهی به چرخه ی حیات فرگمنت

رویکرد من در چرخه ی حیات فرگمنت به دنبال دو هدف است:

  1. مدیریت منطقی چرخه ی حیات فرگمنت منطبق با منطق اکتیویتی
  2. مدیریت منطقی چرخه ی حیات فرگمنت مستقل از چرخه ی حیات اکتیویتی

بیایید هرکدام از این اهداف را با جزئیات بیشتر مورد بررسی قرار دهیم.

با استفاده از مدیریت چرخه ی حیات فرگمنت منطبق بر چرخه ی حیات اکتیویتی من به میزان زیادی پیچیدگی برنامه را کاهش می دهم. توسعه دهندگان تنها نیاز به یادگیری یکی از این دو رویکرد دارند. این موضوع به معنای تلاش کمتر در طول توسعه، نگهداری آسان تر و همچنین درک سریع تر توسط اعضای جدید تیم می شود. من اطمینان کامل دارم که انجام این کار خطر وجود باگ را هرچند ذهنی کاهش می دهد.

ساخت فرگمنت مستقل از چرخه ی حیات اکتیویتی که به نمودار چرخه ی حیات مربوط می شود، هر کدام از آن ها پیچیده بوده اما در انزوا قابل کنترل هستند. با این حال فکر کردن در مورد هر دو چرخه ی حیات و وابستگی بین آن ها خارج از توانایی من است.

من با استفاده از این دو هدف به طرز چشمگیری پیچیدگی فرگمنت را کاهش داده و جذابیت آن را بیشتر نموده ام.

(onCreate(Bundle

به یاد دارید که شما به constructor اکتیویتی دسترسی ندارید بنابراین شما نمی توانید با اعمال dependenceها به اکتیویتی از آن استفاده نمایید؟ خب خبر خوب این است که فرگمنت یک constructor عمومی دارد و ما می توانیم موارد اضافی را نیز تعریف کنیم، و خبر بد اینکه انجام این کار باعث به وجود آمدن باگ هایی می شود و به همین علت ما قادر به انجام آن نیستیم.

اندروید از بین خواهد رفت و سپس مجددا در حین بازسازی فرگمنت ها جریان های ذخیره و بازیابی فراخوانی خواهند شد. مکانیزم بازسازی constructor بدون آرگومان فرگمنت را فراخوانی می کند. بنابراین اگر شما در وهله ی اول از  constructorبا آرگومان در فرگمنت استفاده می کنید و dependence را در آن وارد می کنید تمامی dependenceها پس از ذخیره و بازیابی null خواهد بود. خوب نبود!

بنابراین درست همانند اکتیویتی ها از متد onCreate(Bundle) به عنوان مکان constructorها استفاده خواهید نمود. dependence و  مقداردهی های اولیه نیز در این قسمت انجام می گیرند.

با این حال یکی از مهمترین تفاوت ها این است که شما نباید هیچ عمل مرتبط با viewهای اندرویدی را در این قسمت انجام دهید. این موضوع بسیار مهم است. علت آن در قسمت بعدی مطرح خواهد شد.

در کل onCreate(Bundle) فرگمنت اینگونه خواهد بود:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    getInjector().inject(this); // inject dependencies
 
    if (savedInstanceState != null) {
        mWelcomeDialogHasAlreadyBeenShown = savedInstanceState.getBoolean(SAVED_STATE_WELCOME_DIALOG_SHOWN);
    }
     
    mActivityAsListener = (ActivityAsListener) requireActivity();
 
}

بله موردی که فراموش شد این است که cast نمودن listener interface اکتیویتی نیز در onCreate(Bundle) آورده خواهد شد. اگر آن راه را ترجیح می دهید آن را اعمال کنید تا exception معنی دارتری را نسبت به ClassCastException دریافت نمایید.

(View onCreateView(LayoutInflater, ViewGroup, Bundle

این متد مخصوص فرگمنت است و این برجسته ترین تفاوت بین چرخه ی حیات اکتیویتی و فرگمنت است.

کمی پا فراتر گذاشته و می خواهم بگویم این متد "ریشه ی تمام زیان ها"ی مرتبط با فرگمنت است. در مورد "زیان ها" بعدا بحث خواهم کرد، اما تنها در نظر داشته باشید که اگر از فرگمنت ها استفاده می کنید بهتر است پیچیدگی View onCreateView(LayoutInflater, ViewGroup, Bundle) را دست کم نگیرید.

خب، داستان از چه قرار است؟

اکتیویتی ها با فرض داشتن یک سلسله مراتب از Viewها برای کل چرخه ی حیات با آن در ارتباط هستند.

شما این سلسله مراتب را در onCreate(Bundle) اکتیویتی مقدار دهی می کنید و تا زمانی که از بین بروند در آن قرار دارند. شما می توانید ساختار سلسله مراتبی اکتیویتی را به صورت دستی تغییر دهید اما اندروید هرگز از جانب شما کاری انجام نخواهد داد.

ار سوی دیگر فرگمنت ها اندروید را مسئول تخریب و بازسازی دوباره ی سلسله مراتب View مرتبط با نمونه ی یک فرگمنت به شمار می آورند. به عبارت دیگر فرگمنت ها می توانند در طول چرخه ی حیات خود چندین سلسله مراتب View داشته باشند و این اندروید است که باید تصمیم بگیرد چه زمانی جایگزینی انجام گیرد.

با توجه به این سلسله مراتب را می توان در زمان اجرا جایگزین کرد، حال باید علت عدم وجود onCreate(Bundle) در فرگمنت مشخص باشد. این متد تنها یک مرتبه پس از ضمیمه شدن فرگمنت در میان اکتیویتی فراخوانی می شود، بنابراین نمی تواند از طبیعت پویای سلسله مراتب View فرگمنت پشتیبانی کند.

ورود (View onCreateView(LayoutInflater, ViewGroup, Bundle.

این متد توسط اندروید در هر زمانی که یک سلسله مراتب View نیاز به شکل گیری دارد فراخوانی می شود. وظیفه ی شما این است که سلسله مراتب View را در مکان صحیح ساخته و مقداردهی نمایید و پس از آن از طریق این متد آن را return کنید. اندروید از آن حفاظت خواهد کرد.

قاعده ی اصلی برای پیاده سازی این متد اینچنین است: تمامی اعضای فرگمنت مرتبط با objectها که با سلسله مراتب View در ارتباط هستند می بایست در درون View onCreateView(LayoutInflater, ViewGroup, Bundle) نگهداری شوند. به عبارت دیگر اگر فرگمنت referenceهای Viewها یا objectهای مرتبط را در فیلدهای خود نگهداری می کند باید اطمینان حاصل کنید که در این متد قرار داده شده باشند. این موضوع از اهمیت بالایی برخوردار است.

در کل فرم متداول View onCreateView(LayoutInflater, ViewGroup, Bundle) باید چیزی مشابه خطوط زیر باشد:

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
    View root = inflater.inflate(R.layout.some_layout, container, false);
    mSomeView = rootView.findViewById(R.id.some_view); // must assign all Fragment's fields which are Views
    mLstSomeList = rootView.findViewById(R.id.lst_some_list); // must assign all Fragment's fields which are Views
    mAdapter = new SomeAdapter(getContext()); // must assign all Fragment's fields related to View hierarchy
    mLstSomeList.setAdapter(mAdapter);
    return rootView;
}

نکته ی جالب دیگر در مورد این متد دریافت Bundle با saved state است. حقیقتا من این مورد را آزار دهنده می دانم. بر این اساس توسعه دهندگان اطمینان ندارند که state را دقیقا در کجا باید بازگردانی(restore) کنند، بنابراین آن ها این Bundle را برای ما در چندین متد قرار داده اند تا بتوانیم خودمان از آن استفاده کنیم.

state را در این متد بازگردانی نکنید. علت این امر را زمانی که در مورد onSaveInstanceState(Bundle) بحث می کنیم توضیح خواهم داد.

ویرایش:

کاربر Boza_s6 بازخوردی برای این مقاله در Reddit ارائه نمود و ما بحثی جذاب با هم داشتیم. سوال این بود که آیا روش من می تواند نشت حافظه(memory leak) را در صورت استفاده از listها و adapterها در فرگمنت نشان دهد. با توجه به این مبحث می خواهم مطمئن شوم این موضوع روشن شده است.

اگر قوانینی که در این مقاله مطرح می کنم را به کار گیرید خطر نشت حافظه وجود ندارد. در حقیقت بخشی از دلایل استفاده ی من از این رویکرد کاهش خطر نشت حافظه در فرگمنت است.

قانون View onCreateView(LayoutInflater, ViewGroup, Bundle) این است که همه ی فیلدهای فرگمنت وابسته به سلسله مراتب View باید در این متد قرار بگیرند. این فیلدها شامل list Adapterها، user interaction listenerها و غیره است. تنها راهی که کدهای فرگمنت را قابل نگهداری و عاری از موارد پیچیده و انحرافی می کند، حصول اطمینان از این است که این متد تمامی سلسله مراتب View و objectهای مرتبط را بازنویسی کند.

()onStart

این متد در فرگمنت مسئولیت ها و دستورالعمل هایی دقیقا مشابه ()onStart اکتیویتی دارد. شما می توانید در مقاله ی قبلی من در مورد چرخه ی حیات اکتیویتی در این باره اطلاعات کسب کنید.

بر این اساس فرمت کلی ()onStart در فرگمنت به این صورت است:

@Override
public void onStart() {
    super.onStart();
 
    mSomeView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            handleOnSomeViewClick();
        }
    });
 
    mFirstDependency.registerListener(this);
 
    switch (mSecondDependency.getState()) {
        case SecondDependency.State.STATE_1:
            updateUiAccordingToState1();
            break;
        case SecondDependency.State.STATE_2:
            updateUiAccordingToState2();
            break;
        case SecondDependency.State.STATE_3:
            updateUiAccordingToState3();
            break;
    }
 
    if (mWelcomeDialogHasAlreadyBeenShown) {
        mFirstDependency.intiateAsyncFunctionalFlowAndNotify();
    } else {
        showWelcomeDialog();
        mWelcomeDialogHasAlreadyBeenShown = true;
    }
}

همانطور که می بینید این قسمت محل قرار گیری منطق عملکردی فرگمنت است. واقعیت این است که این رویکرد بین اکتیویتی و فرگمنت سازگار است و از مزایای زیادی برخوردار است.

()onResume

مدیریت این متد مشابه متد ()onResume اکتیویتی است. چقدر عالی است نه!؟

()onPause

حدس بزنید موضوع از چه قرار است؟ این متد نیز همانند متد مشابه خود در اکتیویتی است. تمامی مباحثی که من در مقاله ی فرگمنت اکتیویتی در این باره قید کردم برای ()onPause فرگمنت نیز قابل استفاده است.

()onStop

همچون موارد پیش این متد نیز مشابه ()onStop اکتیویتی است. این متد از الگوی زیر پیروی کند:

@Override
public void onStop() {
    super.onStop();
 
    mSomeView.setOnClickListener(null);
 
    mFirstDependency.unregisterListener(this);
}

بخش جالب و شگفت انگیز ثبت click listener است. من علت این امر را در مقاله ی چرخه ی حیات اکتیویتی توضیح داده ام و آن را بار دیگر تکرار نمی کنم.

با این حال از این فرصت برای پاسخ به سوالی که در مقاله ی قبلی مطرح کرده ام استفاده می کنم. آیا این ثبت الزامی است؟

خب به نظر من اکثر برنامه های اندرویدی این کار را انجام نمی دهند ولی همچنان برنامه به خوبی اجرا می شود بنابراین من فکر می کنم الزامی نیست. پیش از این نیز گفتم اگر شما بدین صورت عمل نکنید می بایست انتظار مشاهده ی خطاها و crashهای مشخصی را توسط notificationهای نادرست دریافت خواهید نمود داشته باشید.

بنابراین اجباری نیست اما این مورد یک بهینه سازی زودهنگام نیست. ما می دانیم که نادر است اما یک مسئله ی واقعی است.

من تلاش می کنم تا آنجا که ممکن است برای سازماندهی لایه ی ارائه ی برنامه از الگوی معماری MVC خود استفاده کنم. در این رویکرد من می توانم تنها با نوشتن یک خط کد ارتباط با UI را "قطع" کنم. بنابراین اگر من بخواهم از این الگو استفاده کنم می بایست همواره UI را در ()onStop قطع کنم.

()onDestroyView

در اکثر موارد شما به هیچ عنوان نباید این متد را override کنید. من حدس می زنم که تعدادی از خوانندگان با خواندن این جمله شگفت زده شده باشند. اما این حقیقت مطلق است.

همانطور که گفتم شما باید فیلدهای فرگمنت را که View هستند درView onCreateView(LayoutInflater, ViewGroup, Bundle) قرار دهید. این نیاز ناشی از این واقعیت است که سلسله مراتب View فرگمنت می تواند دوباره ایجاد گردد، بنابراین reference هر View که شما آن را مقدار دهی اولیه نکرده اید در این متد null خواهد بود. انجام این کار بسیار مهم است زیرا در غیر این صورت ممکن است باگ ها و crashهایی را دریافت نمایید.

اگر شما این کار را انجام دهید فرگمنت منابع قوی این Viewها را تا فراخوانی دوباره ی View onCreateView(LayoutInflater, ViewGroup, Bundle) یا از بین رفتن فرگمنت نگهداری می کند. همچنین کد عاری از خطر نشت حافظه یا هرگونه مشکل احتمالی که من از آن آگاهم خواهد بود.

حال در اینجا یک پیشنهاد مکمل وجود دارد، شما باید تمام فیلدهای View در referenceهای فوق را در ()onDestroyView با null مقداردهی کنید. هدف این است که این منابع در اسرع وقت آزاد شده و به جمع آوری داده های اضافی پس از بازگردانی ()onDestroyView بپردازند. حافظه های مرتبط با این Viewها خیلی زودتر آزاد می شوند.

در حالی که توضیح بالا به نظر منطقی است، این مورد یک مورد کلاسیک از بهینه سازی زودهنگام است. شما در اکثر مواقع به این بهینه سازی نیاز ندارید. بنابراین نیازی نیست با override نمودن ()onDestroyView چرخه ی حیات پیچیده ی فرگمنت کنونی را پیچیده تر کنید.

پس شما به ()onDestroyView نیازی ندارید.

()onDestroy

مشابه متد ()onDestroy در اکتیویتی است و نیازی به پیاده سازی آن در فرگمنت نیست.

(onSaveInstanceState(Bundle

پیاده سازی این متد در فرگمنت همانند پیاده سازی آن در اکتیویتی است:

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putBoolean(SAVED_STATE_WELCOME_DIALOG_SHOWN, mWelcomeDialogHasAlreadyBeenShown);
}

سادگی این متد باعث گمراهی شما نشود. مدیریت نادرست جریان های ذخیره و بازیابی یکی از عوامل اصلی باگ ها و crashها در برنامه های اندرویدی است. این مورد تصادفی نیست بحث در مورد این متد طولانی ترین بخش مقاله ی قبلی من در مورد چرخه ی حیات اکتیویتی را به خود اختصاص داده بود.

بدین ترتیب مدیریت ذخیره و بازیابی در اکتیویتی پیچیده است. ممکن است شما فکر کنید که بدتر از این امکان ندارد اما به طرز شگفت انگیزی در فرگمنت ها اوضاع بدتر هم هست.

Javadoc این متد در فوریه 2011 توسط Dianne Hackborn نوشته شده است و شامل این قسمت واقعا ترسناک است:

این موضوع مربوط به (Activity.onSaveInstanceState(Bundle و بیشتر بحث نیز در این قسمت قرار دارد. با این حال توجه داشته باشید: این متد ممکن است در هر زمانی پیش از ()onDestroy فراخوانی شود. موقعیت های زیادی وجود دارد که در آن یک فرگمنت ممکن است از بین رود(مانند زمانی که بدون نمایش UI در back stack(پشته) قرار می گیرد)، اما تا زمانی که اکتیویتی به طور واقعی نیازی به state نداشته باشد آن را ذخیره نخواهد کرد.

اگر این "نکته" سبب شگفتی شما نشد مشخص است شما هنوز درک عمیقی از چرخه ی حیات اکتیویتی و فرگمنت ندارید.

بر طبق اسناد رسمی "این متد می تواند در هر زمانی پیش از onDestroy() فراخوانی شود"، در اینجا دو مسئله ی مهم مطرح می شود.

اولین مورد، همانطور که Dianne توصیف می کند سلسله مراتب View مرتبط با فرگمنت بدون آنکه state را ذخیره کند می تواند از بین برود. بنابراین اگر خواهان بازگردانی state فرگمنت در View onCreateView(LayoutInflater, ViewGroup, Bundle) هستید، ممکن است خطر override آخرین state با یک مورد قدیمی را پذیرا باشید. انحراف از state اصلی یک باگ بسیار جدی است. علت گفته ی من در مورد بازیابی state در onCreate(Bundle) وجود این مشکل است.

دومین مسئله این است که اگر onSaveInstanceState(Bundle) در هر زمانی پیش از onDestroy() قابل فراخوانی است پس شما هیچ ضمانتی در قبال عدم تغییر state فرگمنت ندارید(مثل جایگزینی فرگمنت های تو در تو).

در حال حاضر من فکر نمی کنم این توصیف صحیح باشد. در حقیقت من معتقدم زمانی که Dianne Hackborn در سال 2011 آن را نوشت در همان زمان هم اشتباه بود. مفهوم "هر زمان" به نوعی غیر متمرکز یا تصادفی است و باعث می شود من گمان کنم هیچ زمانی در آنجا حضور نداشته است. احتمالا تنها وجود چند عامل که بر روی این رفتار تاثیر می گذارند سبب شده است Dianne قصد فهرست نمودن آن را به دلیل عدم اهمیت کافی از نظر وی نداشته باشد.

خب اگر اینچنین بوده او به وضوح اشتباه کرده است.

 توضیحی جایگزین برای این "یادداشت" نیز چندان خوشبینانه نیست. اگر این توضیح در گذشته صحیح بوده است به این معنی است که کارکنان گوگل که این چارچوب را ساخته اند، متوجه چنین رفتاری که دال بر دو مسئله ی ذکر شده است نشده اند. به طور خاص این معنا را می رساند که Bundle حاوی state ذخیره شده نباید هرگز در متد View onCreateView(LayoutInflater, ViewGroup, Bundle) نهاده شود.

خبر خوب این است که این اسناد ممکن است نادرست باشند(حیرت انگیز است، مستندات نادرست می توانند نوید بخش "خبر خوب" باشند)، از بررسی برخی منابع AOSP و کتابخانه های پشتیبان به این نتیجه می رسیم که تضمین onSaveInstanceState(Bundle) در فرگمنت ها اساسا مشابه همین متد در اکتیویتی ها است.

برای اینکه از این باگ در امان باشید این مشکل را در Android issue tracker بازکرده ام و شما تشویق می شوید که به آن ستاره دهید تا طبقه بندی مورد نیاز را سریعتر به دست آورید.

(setRetainInstance(boolean

هیچگاه از فرگمنت های نگهداری شده استفاده نکنید، شما به آن ها نیازی ندارید.

به خاطر داشته باشید در صورت انجام چنین کاری چرخه ی حیات فرگمنت را تغییر داده اید، همچنین تمامی مطالبی که در این مقاله خواندید دیگر تضمین کننده ی هیچکاری نخواهند بود.

چرا فرگمنت ها بسیار پیچیده هستند؟

همانطور که ملاحظه می کنید فرگمنت ها در واقع موجودات پیچیده هستند. من حتی می توانم بگویم بیش از اندازه پیچیده هستند.

بزرگترین مشکل با فرگمنت ها این است که می توانند سلسله مراتب Viewهای خود را از بین برده و به طور مستقل از   objectخودشان دوباره ایجاد کنند. اگر این موضوع نبود چرخه ی حیات فرگمنت تقریبا مشابه اکتیویتی بود.

اکنون سوال بدیهی می تواند این باشد: علت این پیچیدگی چیست؟ خب واضح است که من جواب را نمی دانم و تنها بر اساس درک محدود خود می توانم آن را حدس بزنم. بنابراین بحث را با اندکی تردید ادامه می دهیم!

من فکر میکنم این مکانیزم برای بهینه سازی مصرف حافظه مطرح شده است. توانایی تخریب سلسله مراتب View فرگمنت اجازه می دهد مثلا زمانی که فرگمنت visible نیست حافظه اندکی آزاد شود، اما پس از آن این سوال مطرح می شود: پشتیبانی از پیاده سازی فرگمنت ها به منظور جریان استاندارد ذخیره و بازیابی است، چرا گوگل هنوز مکانیزم دیگری برای اینچنین هدفی اختراع نکرده است؟

من نمیدانم...

با این حال چیزی که می دانم این است که حتی FragmentStatePagerAdapter به این بهینه سازی تکیه نمی کند و فرگمنت ها مجبور به انجام کامل ذخیره و بازیابی است.

نگرانی من این است که تمام این مکانیزم یک نمایش خیره کننده از خطرات مرتبط با بهینه سازی زودهنگام است این موضوع واقعا ضروری نیست، مفید نبوده و پیچیدگی استفاده از فرگمنت ها را به طور نجومی افزایش می دهد.

به نظر من مشابه تمام مشکلات اضافی بود که با فرگمنت در طی سال ها تجربه کردیم و همگی از "ریشه ی همه ی زیان ها" تشکیل شده اند.

برنامه نویسان زمان زیادی را صرف فکر و نگرانی در مورد بخش های غیرضروری برنامه ی خود می کنند و این تلاش ها  برای بهره وری، حقیقتا تاثیر منفی در نتیجه و نگهداری و اشکال زدایی دارد. ما باید سودآوری های کوچک را فراموش کنیم صحبت من در مورد 97 درصد زمان است، بهینه سازی زودهنگام ریشه ی تمام زیان هاست. ما نباید فرصت خود را صرف این 3 درصد غیر ضروری کنیم.    Knuth, Donald

و حقیقت غم انگیز این است که به نظر می رسد توسعه دهندگان گوگل خودشان دیگر چرخه ی حیات فرگمنت را درک نمی کنند.

گوگل LiveData Architecture Component را با یک باگ جدی مطرح شده توسط Christophe Beyls در این مقاله منتشر کرد. اگر تنها یک توسعه دهنده در این زمینه کار می کرد که درک کافی از چرخه ی حیات فرگمنت داشته باشد این مشکل در مرحله ی طراحی آشکار می شد.

چند ماه طول کشید تا گوگل این باگ را رفع کند. نهایتا در طول Google IO 18 اعلام کردند که این مشکل برطرف شده است. این رفع مشکل نیز معرف چرخه ی حیات دیگری برای سلسله مراتب View فرگمنت بود.

بدین ترتیب اگر شما از LiveData Architecture Component استفاده می کنید اکنون باید بدانید که در اینجا دو چرخه ی حیات متمایز مرتبط با object فرگمنت وجود دارد. تمامی این موارد من را به شدت غمگین می سازد.

نتیجه گیری

بیایید این مقاله را جمع بندی کنیم.

فرگمنت ها پیچیده هستند. چرخه ی حیات آن ها همچون علوم موشکی است. تنها بدی فرگمنت ها اسناد رسمی آن ها است. توسعه دهندگان گوگل درک کاملی از چرخه ی حیات فرگمنت ندارند و همچنان به افزایش پیچیدگی این موجود ادامه می دهند.

گفتنی ها را گفتم، من سالهاست که از فرگمنت استفاده می کنم و در آینده نیز از آن استفاده خواهم کرد. در مقایسه با رویکرد "یک اکتیویتی در هر صفحه" فرگمنت می تواند تجربه ی کاربری بهتری فراهم کند و سبب شود کاربر حین استفاده از برنامه احساس خوبی داشته باشند.

من برای استفاده از فرگمنت از رویکرد مطرح شده در این مقاله استفاده می کنم. ممکن است 100 درصد موارد استفاده را پوشش ندهد اما در سال های گذشته برای من عالی کار کرده است.

برای این رویکرد دو مزیت وجود دارد: استفاده از چرخه ی حیات فرگمنت بسیار مشابه اکتیویتی بوده و همپنین مستقل از آن است. شما ممکن است متوجه شده باشید که در هیچ قسمتی از مقاله من وضعیت اکتیویتی را محصور قید نکرده ام. تا زمانی که به این رویکرد پایبند باشم موارد مرتبط با اکتیویتی برای من اهمیتی ندارند. من حتی اهمیت نمی دهم که سطح فرگمنت بالاست یا نه.

بنابراین من تعدادی از متدها را override کردم و هیچ وابستگی به ورودی بین چرخه ی حیات ندارم. این موضوع باعث می شود پیچیدگی فرگمنت ها برای من قابل کنترل باشد.

با این حال فرگمنت ها بسیار پیچیده هستند و من تقریبا مطمئن هستم بعضی از نکات مهم را در هنگام نوشتن مقاله فراموش کرده ام. از این رو اگر شما موردی برای افزودن دارید یا مایل به اشاره به نقایص موجود در این مقاله هستید لطفا در بخش نظرات آن را قید کنید.


کلیدواژه: Fragment lifecycle activity activity lifecycle

منابع: www.techyourchance.com

ارسال دیدگاه:
برای ارسال دیگاه باید به سیستم وارد شوید و یا ثبت نام کنید. ثبت نام چند لحظه بیشتر زمان شما را نمیگیرد.