جشنواره تخفیف‌های تابستانی به مدت یک هفته! کد تخفیف ۲۵% : SUMMER 

Activity Life-Cycle اندروید برای توسعه دهندگان حرفه ای

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

اکنون سال 2018 است و من در حال نگارش یک مقاله در مورد life-cycle اکتیویتی ها در اندروید هستم. به من اعتماد داشته باشید من خود حیرت زده شدم.

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

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

 من قصد دارم روش خود را برای مدیریت life-cycle اکتیویتی بیان کنم. این مقاله بسیار ساده تر از آن چیزی است که شما در آموزش ها و مستندات رسمی می یابید و اکثریت حقه ها و موارد مهم را مورد بررسی قرار می دهد.

life-cycle اکتیویتی هنوز به عنوان یک مسئله مطرح است:

دو رویدادی که طی یک هفته اتفاق افتاد چشم من را به این حقیقت باز کرد که همچنان life-cycle اکتیویتی به عنوان یک مسئله بین توسعه دهندگان اندروید مطرح است.

چند هفته پیش یک redditor مقاله ای در مورد life-cycle اکتیویتی در زیر مجموعه ی androiddev سایت Reddit به اشتراک گذاشته بود. این مقاله بر پایه ی پاسخ قدیمی است که من در سایت StackOverFlow نوشته ام، من خیلی سریع متوجه شدم که یکی از روش های پیشنهادی نویسنده نادرست است، پیامی نهاده و سایر خوانندگان را در آن مورد آگاه ساختم.

تعدادی از redditorها (اعضای سایت Reddit) به دیدگاه و و پیام من پاسخ دادند و منجر به بحثی طولانی و روشنگری بسیار در این زمینه شد. در این بحث ما چندین حقیقت جالب در مورد life-cycle اکتیویتی یافتیم.

  1. life-cycle اکتیویتی گاهی حتی ذهن توسعه دهندگان با تجربه را نیز آشفته می سازد.
  2. مستندات رسمی همچنان شامل اطلاعات منسوخ و قدیمی و پرنقص در مورد life-cycle اکتیویتی هستند.
  3. حتی توسعه دهندگانی که آموزش های رسمی گوگل را می نویسند ممکن است درکی کامل از life-cycle اکتیویتی و مفهوم آن نداشته باشند.

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

در طی بررسی سریع کد متوجه شدم که یکی از پشتیبان های قبلی تصمیم داشته است تا اشتراک اکتیویتی ها با EventBus در متد (onCreate(Bundle صورت گیرد نه در متد ()onDestroy. انجام این دستورالعمل مطمئنا با مشکلاتی همراه بود در نتیجه من پیشنهاد دادم این مورد را دوباره پیاده سازی کنند.

برداشت من از life-cycle اکتیویتی

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

من قصد ندارم مستند و آموزشی جایگزین برای life-cycle اکتیویتی ارائه دهم چرا که این بحث گسترده بوده و در این مقاله نمی گنجد، در عوض آن قصد دارم چگونگی تقسیم بندی منطقی بین متدهای life-cycle را به منظور دستیابی به ساده ترین طرح و جلوگیری از مشکلات معمول مطرح کنم.

فرض می کنیم شما به طور کلی با life-cycle اکتیویتی آشنا هستید.

(onCreate(Bundle :

framework اندروید تمامی اکتیویتی ها را خود ایجاد می کند در نتیجه برای اکتیویتی ها به constructorها نیازی ندارد. خب حال اکتیویتی از کجا شروع به فعالیت می کند؟

اکتیویتی از متد (onCreate(Bundle شروع به فعالیت می کند. این متد میزبان تمام منطق موجود در constructor است.

در حالت ایده آل یک constructor تنها فیلدهای objectها را با اعمال dependencieها مقدار دهی اولیه می کند:

public ObjectWithIdealConstructor(FirstDependency firstDependency, 
                                  SecondDependency secondDependency) {
    mFirstDependency = firstDependency;
    mSecondDependency = secondDependency;
}

[برای درک بهتر این مسئله که چرا constructor بالا ایده آل است مقاله ی Misko Hevery را بخوانید. حتی با اینکه Misko در مورد مسائل مرتبط با "انجام کار زیاد توسط constructorها" در زمینه ی تست واحد بحث می کند، این constructorها به طور مساوی در خوانایی و نگهداری کد بد هستند]

برای مقداردهی اولیه ی فیلدها (onCreate(Bundle دو مسئولیت بیشتر از چارچوب اندروید به عهده دارد: بازگردانی state و فراخوانی ()setContentView.

بنابراین قابلیت کلی موجود در متد (onCreate(Bundle باید اینچنین باشد:

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
 
    getInjector().inject(this); // inject dependencies
 
    setContentView(R.layout.some_layout);
     
    mSomeView = findViewById(R.id.some_view);
 
    if (savedInstanceState != null) {
        mWelcomeDialogHasAlreadyBeenShown = savedInstanceState.getBoolean(SAVED_STATE_WELCOME_DIALOG_SHOWN);
    }
 
}

ظاهرا یک سایز متناسب همه نیست و شما باید اندکی تفاوت ساختاری قائل شوید. این موضوع تا زمانی که با موارد زیر برخورد نداشته باشید خوب است:

  • اشتراک در موارد قابل مشاهده
  • جریان عملیاتی
  • مقداردهی اولیه جریان های عملیاتی ناهماهنگ
  • تخصیص منابع

هر زمانی که باید تصمیم بگیرم این قطعه از منطق متعلق به (onCreate(Bundle است یا نه، از خود این سوال را می پرسم: آیا این منطق با مقداردهی اولیه ی objectها مرتبط است یا نه؟ اگر پاسخ منفی باشد جای دیگری برای ان پیدا می کنم.

زمانی که اکتیویتی به وسیله ی framework معرفی شده و متد (onCreate(Bundle برای اجرای آن تکمیل شد به وضعیت "ایجاد شده" در می آید.

اکتیویتی "ایجاد شده" نباید به تخصیص منابع بپردازد و نباید گیرنده ی رخدادهایی از دیگر objectهای سیستم باشد. به این معنا که وضعیت ایجاد شده با عنوان حالت "غیرفعال و جدا شده" شناخته شود.

 ()onStart :

در بعضی مواقع با توجه به تعامل کاربر، framework اندروید متد ()onStart اکتیویتی را فرا میخواند و فعال بودن آن را اعلام می دارد. این متد مکان مناسبی برای برگرداندن مقداردهی اولیه ی صورت گرفته است اما اکتیویتی "ایجاد شده" برای چرخه غیرفعال و جدا شده است.

بدین معنی که به الزامات هر یک از اکتیویتی های خاص وابسته است. خصوصیات کلی منطق که در متد ()onStart قرار دارد عبارتند از:

  1. ثبت View click listeners
  2. اشتراک در موارد قابل مشاهده(مشاهدات کلی، به غیر از Rxهای ضروری)
  3. برگرداندن حالت جاری به UI(به روز رسانی UI)
  4. جریان عملیاتی
  5. مقداردهی اولیه ی جریان های عملیاتی ناهماهنگ
  6. تخصیص منابع

ممکن است شما از موارد اول و دوم بدین علت که بیشتر مستندات و آموزش ها هر دو را در (onCreate(Bundle انجام می دهند شگفت زده شده باشید. من این موضوع را به عنوان یکی دیگر از سوء تفاهمات پیچیده ی life-cycle اکتیویتی می بینم.

بار دیگر در ادامه ی مطلب در متد ()onStop به این موضوع خواهیم پرداخت.

بدین صورت ساختار کلی متد ()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;
    }
}

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

پس از آنکه متد ()onStart توسط framework اندروید فراخوانی می شود وضعیت از "ایجاد شده" به "آغاز شده" تغییر حالت می دهد. این وضعیت وضعیتی کاربردی است و می تواند با اجزای دیگر سیستم همکاری کند.

()onResume :

اولین قانون در مورد متد ()onResume این است که شما به این متد نیازی ندارید. دومین قانون نیز همانند قانون اول است. سومین قانون احتمال این که شما در شرایط خاص به این متد نیاز داشته باشید را بیان می کند.

هرچند من تنها در یکی از کدهایی که در حال کار بر روی آن هستم جست و جو کردم، اما به طور متوسط از هر 5 خط کد من سی و دو override از ()onStart یافتم. این موضوع در حدود 150 خط کد در تمام متدهای ()onStart ترکیبی به من داد.

در مقابل من تنها دو override از متد ()onResume یافتم که شامل 8 خط کد بود. در هر دو مورد این متدها برای از سر گیری انیمیشن ها و ویدئوها روی صفحه بود.

دیدگاه من درباره ی ()onResume در این جمله خلاصه می شود که این متد می بایست تنها برای شروع و شروع مجدد برخی عملکردهای حرکتی بر روی صفحه مورد استفاده قرار گیرد. در بخش ()onPause علت اینکه چرا شما می خواهیم این کار را به جای متد  ()onStart در متد ()onResume انجام دهید بحث خواهیم کرد.

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

()onPause:

در این متد شما باید در انیمیشن ها و ویدئوهایی که در متد ()onResume آغاز شده یا در حالت شروع مجدد قرار داده بودید مکث ایجاد کرده یا به طور کامل اعمال توقف کنید. همانند متد ()onResume شما تقریبا هیچگاه به ()onPause نیازی ندارید.

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

 با این حساب اگر شما تمایل دارید که انیمیشن یا ویدئو در حالت چند پنجره ای نیز ادامه داشته باشد نباید آن را در ()onPause مکث کنید. در این مورد منطق را از ()onResume()/onPause به ()onStart()/onStop منتقل کنید.

یک مورد خاص که توجه من را به خود جلب کرده دوربین بود. از آنجایی که دوربین تنها منبع اشتراکی بین برنامه ها است، معمولا اینگونه است که شما آن را در متد ()onPause آزاد می کنید.

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

پس از این متد اکتیویتی از وضعیت "شروع مجدد" به حالت "آغاز شده" تغییر حالت می دهد.

()onStop :

در این متد شما تمامی viewهای قابل مشاهده و listener را لغو می کنید و تمامی منابعی که در ()onStart تخصیص داده شده بودند را آزاد می نمایید.

من معمولا با چنین خطوطی آن را به پایان می رسانم.

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

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

در اینجا این سوال مطرح می شود که چرا حذف در ()onStop صورت می گیرد نه در ()onPause یا ()onDestroy؟

توضیح اینکه چرا ()onPause برای این منظور گزینه ی خوبی نیست اندکی مشکل است اما در مجموع حالت چند پنجره ای یک توضیح سریع و مفید ارائه می دهد.

زمانی که اکتیویتی در حالت چند پنجره ای به نمایش گذاشته می شود کاربر حتی اگر در آن لحظه با آن تعاملی نداشته باشد انتظار دارد که اکتیویتی آماده به کار باشد. در غیر این صورت چه لزومی دارد آن ها برنامه را وارد حالت چند پنجره ای کند و در صفحه نمایش نگاه دارد؟

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

حذف در ()onDestroy نیز گزینه ی مناسبی نیست.

ببینید زمانی که برنامه ی شما به وسیله ی کاربر به background می رود(مثلا بر روی دکمه ی home کلیک می شود) متد ()onStop اکتیویتی فراخوانی می شود تا وضعیت به حالت "ایجاد شده" دربیاید. اکتیویتی می تواند روزها و حتی هفته ها در این وضعیت باقی بماند.

اگر mFirstDependency جریان پیوسته ی رویدادها را تولید کند این اکتیویتی می تواند هفته ها در جریان باشد حتی اگر در این مدت کاربر هیچ تعاملی با آن نداشته باشد. این موضوع می تواند یک عمل غیر مسئولانه به حساب بیاید چرا که سبب اتلاف عمر باتری کاربر می شود.

به طور مشابه ثبت در mFirstDependency می تواند با مصرف بالای حافظه همراه باشد. در این مورد زمانی که اندروید با کمبود حافظه مواجه می شود کل برنامه ی موجود در background را از بین می برد.

بنابراین ()onPause و ()onDestroy مکان مناسبی برای حذف وابستگی ها خارجی به حساب نمی آیند بنابراین شما باید این موارد را در ()onStop انجام دهید.

یک نکته در مورد حذف View objectها

با توجه به طراحی ناخوشایند UI اندروید سناریوهای عجیب و غریبی مانند مواردی که در این سوال توضیح داده شده است می تواند رخ دهد. راه های متفاوتی برای اعمال وجود دارد اما حذف از طریق عناصر UI در ()onStop از بهترین های IMHO به شمار می آید.

به طور خلاصه می توانید این سناریوی نادر را نادیده بگیرید. کاری که بسیاری از توسعه دهندگان اندروید انجام می دهند در اثر آن کاربران شما گاهی crashهای عجیب و غریبی را تجربه خواهند کرد.

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

پس از انجام متد ()onStop اکتیویتی از وضعیت "آغاز شده" به "ایجاد شده" باز می گردد.

()onDestroy :

این متد هرگز نباید override شود. هرگز به معنای واقعی کلمه!

من تمام پروژه های خود را بازبینی کردم حتی در یک مورد هم من متد () onDestroy را override نکرده بودم. این در صورتی است که شما به گفته های من در مورد وظایف (onCreate(Bundle دقت کرده باشید. از آنجایی که (onCreate(Bundle تنها مقدار دهی اولیه ی objectهای اکتیویتی را انجام می دهد و عمل دیگری صورت نمی گیرد در اینجا هیچ کاری برای انجام نداریم.

هنگام بازبینی کدهای جدید اندرویدی من معمولا به دنبال اشتباهات رایج و خارج از الگو هستم. این موضوع به سرعت کیفیت کد را به من نشان می دهد. Override نمودن متد ()onDestroy جز اولین مواردی است که من به دنبال آن هستم.

(onSaveInstanceState(Bundle :

آخرین مورد life-cycle اکتیویتی که غالبا از آن استفاده می کنم متد (onSaveInstanceState(Bundle است.

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

تنها کاری که برای این متد باید انجام دهید این است که state مورد نظر خود را برای نگهداری در Bundle مهیا شده قرار دهید:

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

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

اگر پس از اتمام این متد تراکنش فرگمنت را انجام دهید برنامه با خطای IllegalStateException و مطابق انتظار crach خواهد کرد. این بحث به خودی خود سرشار از زشتی هاست و من نمیخواهم به طور عمقی به آن بپردازم. اگر شما متوجه منظور من نمی شوید می توانید این مقاله را که توسط Alex Lockwood نوشته شده است بخوانید(هرچند ممکن است امروزه کمی قدیمی به نظر بیاید).

چرا فراخوانی (onSaveInstanceState(Bundle پیش از ()onStop حائز اهمیت است؟

اکتیویتی پس ذخیره ی state و objectهای خارجی و viewهای داخلی باقی مانده و از بین نمی رود. بنابراین اگر تراکنش فرگمنت به دلیل درخواست فراخوانی متقابل یا تعامل کاربر بین (onSaveInstanceState(Bundle و ()onStop انجام شود برنامه crash خواهد کرد.

در اینجا چندین رویکرد برای مدیریت این مشکلات مطرح می شود:

  1. هیچ کاری نکنید و قبول کنید که ممکن است crash وجود داشته باشد.
  2. تراکنش فرگمنت را با استفاده از متد () commitAllowingStateLoss انجام دهید و قبول کنید که ممکن از برخی از stateها از بین بروند.
  3. به طور کلی از فرگمنت استفاده نکنید.
  4. هر مورد را به طور جداگانه handle کنید و احتمال از بین رفتن بعضی از این مشکلات را بدهید.

آیا من قبلا ذکر نکردم که جبران این خسارت ها و همچنین سبک سنگین کردن مسائل روزمره کار را برای توسعه دهندگان حرفه ای اندروید سخت می کند؟

خبر خوب این است که پس از سال ها جدال گوگل در نهایت دریافت که این مورد اساسا یک bug بوده است.

به همین دلیل ترتیب فراخوانی متدها در Android P تغییر داده خواهد شد و ()onStop پیش از (onSaveInstanceState(Bundle فراخوانی می شود.

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

ویرایش اول

Redditor Boza_s6 بازخوردی ارزشمند از این مقاله را ارائه داد:

اما حتی با وضعیت فعلی من فکر نمی کنم مشکلی پیش بیاید اگر همه ی موارد در onStop لغو شده باشد، زیرا AFAIK onSaveInstanceState و onStop به عنوان قسمتی از یک پیام فراخوانی می شوند بنابراین هیچ شانسی برای اجرای برنامه ای بین آن ها وجود نخواهد داشت.   (Boza_s6)

من هیچوقت در مورد آن نه چیزی خوانده ام نه چیزی شنیده ام اما فکر میکنم به اندازه ی کافی جالب و مهم است که تحقیقی کامل در این زمینه انجام دهم. بنابراین نسخه ای از AOSP را گشودم و بعد از مدتی آن را به پایان رساندم. متد ()ActivityThread#performStopActivityInner را نگاهی بیندازید.

هر دو متد ()onStop و (onSaveInstanceState(Bundle از این متد سررشته گرفته اند. من تمام راه های اجرایی مربوطه را بررسی کردم تا اطمینان یابم هیچ Runnables posting وجود ندارد و نداشت.

سپس تاریخچه ی AOSP Git را تا زمان فراخوانی متدها با هم بررسی نمودم.

خرسندم که بگویم انتخاب این پیاده سازی به روزهای Honeycomb بر می گردد. به این معنا که اگر شما از رویکردهای ارائه شده در این لیست استفاده کنید هنگام تراکنش فرگمنت پس از ذخیره ی state از crashها در امان خواهید بود.

من توصیه نمی کنم که برنامه ی خود را بر اساس جزئیات غیرقانونی بنویسید اما از آنجایی که ترتیب متدها در Android P تغییر خواهد کرد شما هیچ مشکلی ندارید.

بنابراین شما می توانید تراکنش های فرگمنت را بعد از ذخیره ی state فراموش کنید. شما از خطا ایمن هستید فقط اطمینان حاصل کنید که رویکردی را که من ارائه کرده ام دنبال می کنید و اکتیویتی شما پس از ()onStop غیرفعال و جدا شده می شود.

ویرایش دوم

ویرایش اول کاملا صحیح نبود.

هنوز یک مورد دیگر وجود دارد که توسط Redditor RaisedByTheInternet مطرح شده است. [به هر حال من قبلا نگفتم که زیر شاخه ی androiddev یکی از بهترین منابع برای توسعه دهندگان حرفه ای اندروید است؟]

همانطور که در صفحه ی رسمی مدیریت life-cycle آمده است:

API 23 و پایین تر اندروید state یک اکتیویتی را ذخیره می کند حتی اگر این اکتیویتی توسط اکتیویتی دیگر مخفی شده باشد. به عبارت دیگر اندروید ()onSaveInstanceState را فراخوانی می کند اما لزومی ندارد که ()onStop را نیز فراخوانی کند. در اصل یک وقفه ی طولانی ایجاد می کند به طوری که مشاهده کننده فکر می کند life-cycle همچنان فعال است حتی اگر وضعیت UI آن را تغییر ندهد.

پس اگر به معنای واقعی اکتیویتی متوقف شده باشد هیچ وقفه ای بین ()onStop و (onSaveInstanceState(Bundle وجود ندارد. با این حال در API 23 و پایین تر (onSaveInstanceState(Bundle می تواند بدون ()onStop فراخوانی شود اگر اکتیویتی توسط اکتیویتی دیگر مخفی شده باشد.

پیامدهای این موضوع چیست؟

در ابتدا "ویرایش اول" برای API 23 و پایین تر نادرست است چرا که همچنان احتمال از دست دادن state در برخی از اکتیویتی ها مخفی شده وجود دارد. این موضوع بسیار مهم است و من  از redditor RaisedByTheInternet برای اصلاح این مشکل بسیار سپاسگزارم.

ثانیا شما همچنان نیاز دارید تا استراتژی خود را از بین چهارگزینه ای که در بالا لیست کرده ام انتخاب کنید(قبل از دو ویرایش).

با توجه به این واقعیت که "مخفی شدن اکتیویتی توسط اکتیویتی دیگر" نادر است و در API 24 و بالاتر دیگر مطرح نیست، گزینه های 1 و 2 برای من خوشایند تر هستند.

نتیجه گیری:

خب زمان اتمام مقاله است.

به نظر نمی رسید ساده و کوتاه باشد اما من فکر می کنم همچنان از منابع موجود ساده تر و کامل تر است(از جمله از مستندات رسمی متاسفانه).

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

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

زمانی که پشتیبانی از چند صفحه در Nougat فراهم شد، این رویکردی که در این مقاله با شما به اشتراک گذاشتم نیاز به هیچ تغییر و سازگاری نداشت. این موضوع اساسا وجود بینش عمیقتری در مورد life-cycle اکتیویتی نسبت به موارد توصیه شده رسمی را تایید کرد.

علاوه براین تغییر در ترتیب بین متدهای (onSaveInstanceState(Bundle و ()onStop در اکتیویتی ها در Android P این رویکرد را برای کار با فرگمنت ها امن تر می سازد.

من فکر نمی کنم این موضوع تصادفی باشد!

 

 

امیدوارم مطالب ارائه شده مفید واقع شوند...


کلیدواژه: android اندروید activity اکتیویتی fragment فرگمنت activity life-cycle

منابع: www.techyourchance.com

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