آشنایی با Android Lifecycle

نویسنده : سید ایوب کوکبی ۱۴ تیر ۱۳۹۸
آشنایی با Android Lifecycle

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

در این مقاله منظور از کامپوننت هر چیزی می‌تواند باشد: اکتیویتی، فرگمنت، سرویس یا حتی خودِ برنامه و پراسس‌های زمینه. هر کامپوننت چرخۀ حیات و عمری دارد و تنها در طی این چرخه می‌تواند فعالیت داشته باشد. هر تغییر و رویدادی در این چرخه با استفاده از متدهای کال‌بک به اطلاع شما می‌رسد؛ مثلاً onCreate.

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

بخش اول: برنامه‌ای با یک اکتیویتی

سناریوی اول: برنامه به اتمام رسیده (Finish) یا ریستارت می‌شود (Restart).

این سناریو زمانی اتفاق می‌افتد که کاربر دکمۀ back را بزند یا زمانی که متد Activity.finish فرخوانی شود. در تصویر پایین توالی فرخوانی متدهای چرخۀ حیات در یک برنامۀ Single Activity را می‌بینید:

با اجرای برنامه به ترتیب متدهای onCreate و سپس onStart و در ادامه onResume فراخوانی می‌شوند. در حین اجرای برنامه کاربر دکمۀ Back (بازگشت) را می‌زند؛ اکنون متدهای onPause و onStop و نهایتاً onDestroy فراخوانی می‌شوند. در صورت اجرای مجدد برنامه سه متدی که در شروع اجرای برنامه فرخوانی شدند مجددا فراخوانی می‌شوند.

مدیریت State:

  • onSavedInstanceState فراخوانی نمی‌شود (وقتی در برنامه فقط یک اکتیویتی دارید و آن را هم با دکمۀ Back بسته‌اید عملاً با فرخوانی متد onDestroy چرخۀ حیات برنامه به پایان می‌رسد و دیگر نیازی به ذخیرۀ state نیست؛
  • onCreate: اینجا هم چون برنامه به پایان رسیده و در اجرای مجدد هیچ باندلی (Bundle) وجود ندارد، بازیابی State بی معناست.

سناریوی دوم: کاربر به برنامۀ دیگری سوئیچ می‌کند

این سناریو در دو حالت اتفاق می‌افتد. یا کاربر در حین اجرای برنامۀ دکمۀ Home را می‌زند و به صفحۀ اصلی برمی‌گردد و یا به برنامۀ دیگری سوئیچ می‌کند. سوئیچ کردن به برنامۀ جدید، هم می‌تواند از طریق کلیک روی نوتیفیکیشن باشد و هم انتخاب از لیست برنامه‌ها در منوی Overview.

در این سناریو اندروید فعالیت اکتیویتی را به پایان نمی‌رساند؛ یعنی finish نمی‌کند بلکه با فرخوانی متد onStop فعالیت آن را تعلیق می‌کند.

بخوانید  آموزش گیت - قسمت پنجم

مدیریت State:

اندروید در صورت کمبود منابع می‌تواند برنامه‌های که در زمینه قرار دارند را ببندد. وقتی کاربر دکمۀ Home را می‌زند یا به برنامۀ دیگری سوئیچ می‌کند اندروید متد onSavedInstanceState را فرخوانی کرده و وضعیت برنامه را در آن ذخیره می‌کند تا در صورت kill شدن برنامه و بازگشت کاربر به آن امکان بازیابی وضعیت قبلی وجود داشته باشد. در صورتی که برنامه kill نشود، اکتیویتی به همان صورتی که بوده در حافظه باقی می‌ماند و نیازی به بازیابی و مقداردهی مجدد کامپوننت‌های قبلی نیست.

سناریوی سوم: Configuration تغییر می‌کند

این سناریو زمانی اتفاق می‌افتد که کانفیگ سیستم‌عامل مثلاً حالت صفحه از عمودی به افقی یا به‌عکس تغییر می‌کند و یا در تنظیمات دستگاه پارامترهایی مثل زبان تغییر می‌کنند. حالت بعدی اجرای این سناریو زمانی است که کاربر در محیط multi-window اندازۀ پنجره را تغییر می‌دهد.

مدیریت State

تغییر در کانفیگ این انتظار را برای کاربر به وجود می‌آورد که دقیقاً از همان جایی که بوده ادامه دهد. به محض تغییر در Configuration برنامه وضعیت فعلی را با متد onSavedInstanceState ذخیره کرده و سپس متد onDestroy را فرخوانی می‌کند. پشت بند این متد به صورت خودکار متدهای onCreate و سپس onStart فرخوانی می‌شوند و بعد از این دو متد onRestoreInstaneState برای بازیابی وضعیت قبلی وارد عمل می‌شود. در نهایت متد onResume فراخونی می‌شود. باندلی که در متد onCreate وجود دارد همان باندلی است که در onRestoreInstanceState است.

سناریوی چهارم: برنامه توسط سیستم‌عامل Pause می‌شود

سناریوی حاضر در چهار حالت مختلف به وجود می‌آید:

  • فعال کردن Multi-window (در +API 24) و از دست دادن focus برنامه؛
  • اپلیکیشن دیگری به صورت خودکار اجرا شده و بر روی برنامۀ ما قرار می‌گیرد (مثلاً دیالوگ خرید، پیغام runtime permission، زنگ خوردن گوشی و … )
  • یک Intent Chooser مثل Share Dialog ظاهر می‌شود.

به صورت خلاصه هر چیزی که فوکوس برنامه را از بین ببرد باعث اجرای این سناریو و فرخوانی متد Pause خواهد شد.

در این حالت هیچگونه مدیریت State ای لازم نیست. بعد از ناپدید شدن پنجره‌ای که فوکوس برنامه را به هم زده متد onResume به صورت خودکار اجرا شده و کنترل به برنامۀ شما باز می‌گردد. البته به این موضوع دقت کنید که دیالوگ‌های متعلق به خودِ برنامه مثلاً AlertDialog ها یا DialogFragment ها باعث pause شدن برنامه نمی‌شوند. این اتفاق تنها زمانی اتفاق می‌افتد که برنامۀ دیگری عامل ایجاد وقفه باشد. همچنین نمایش نوتیفیکیشن‌ها و کشیدن آن به پایین که روی برنامه را هم می‌گیرد باعث فرخوانی متد pause نمی‌شود.

بخوانید  پیغام خطا را اینطور بنویسید. (11 نکته برای نوشتن پیغام خطای کاربرپسند)

بخش دوم: برنامه‌ای با چند اکتیوینی

در تصاویر این بخش هر جایی که دو باکس کنار هم آمده یعنی متدهای آن باکس‌ها به صورت موازی اجرا می‌شوند. اینکه در زمان اجرا ابتدا کدام یک از عنصر اجرا شود معلوم نیست و در کنترل ما هم نیست. ولی ترتیب فرخوانی متدها درون هر باکس به صورت تضمین شده همانطوری است که می‌بینید.

سناریوی اول: کاربر بین دو اکتیویتی سوئیچ می‌کند

وقتی کاربر در زمان اجرای یک اکتیویتی به اکتیویتی دیگری سوئیچ می‌کند متدهای onPause, onStop و onSavedInstanceState در اکتیویتی جاری به ترتیبی که در تصویر مشاهده می‌کنید فرخوانی می‌شوند و همزمان متدهایی onCreate, onStart و onResume در اکتیویتی مقصد اجرا می‌شوند. همانطور که بالاتر گفتیم ترتیب اجرای متدها درون هر باکس تضمین شده است ولی اینکه در حالت موازی ابتدا کدام متدها اجرا شوند معلوم نیست و کنترل آن در اختیار سیستم‌عامل است.

بعد از نمایش اکتیویتی ۲ وقتی کاربر دکمۀ Back را بزند مجدداً متدهای onPause, onStop و onDestroy در این اکتیویتی فرخوانی شده و همزمان با آن متدهای onRestart, onStart و نهایتاً onResume در اکتیویتی اول اجرا می‌شود. در صورتی که دوباره Back زده شود متدهای onPause, onStop و onDestroy به برنامه خاتمه می‌دهد. و این در صورتی است که اکتیویتی ۱، اکتیوتی اصلی برنامه باشد (یعنی همان چیزی که در شروع اجرای برنامۀ نمایان می‌شود).

مدیریت State

در این سناریو به این نکته دقت کنید که onSavedInstanceState فرخوانی می‌شود ولی onRestoreInstanceState نه. زمانی که اکتویتی دوم فعال است اگر تغییراتی در کانفیگ به وجود آید اکتیویتی اول فقط destroyed و recreate می‌شود؛ به همین خاطر ذخیرۀ state بسیار مهم است. در صورتی که اندروید برای آزاد کردن منابع مجبور شود پراسس برنامه را ببندد باز هم نیازمند بازیابی وضعیت قبلی خواهید بود.

سناریوی دوم: تغییر در کانفیگ زمانی که اکتیویتی‌هایی در بک استک وجود داشته باشد

اکتیویتی ۱ در بک استک است و اکتیویتی ۲ در حال نمایش است. کاربر در چنین وضعیتی صفحۀ گوشی را می‌چرخاند. این یعنی تغییر کانفیگ صورت گرفته و متدهای onPause, onStop, onSavedInstanceState, onDestroy, onCreate, onStart, onRestoreInstanceState و نهایتاً onResume بر روی اکتیویتی دوم به ترتیبی که در تصویر مشاهده می‌کنید فرخوانی می‌شود. اکنون کاربر دکمۀ Back را می‌زند. اکتیویتی دو onPause, onStop و onDestroy می‌شود و اکتیویتی یک onDestroy, onCreate, onStart, onRestoreInstanceState و onResume خواهد شد.

مدیریت State

ذخیره کردن State برنامه تنها برای زمانی نیست که اکتیویتی در حالت Foreground قرار دارد. تمامی اکتیویتی‌هایی که در Stack قرار دارند باید بتوانند وضعیت قبلی خود را بعد از تغییر کانفیگ برگردانند تا UI مجددا ساخته شود. به این موضوع هم توجه داشته باشید که سیستم هر زمانی حق دارد پراسس برنامۀ شما را از حافظه خارج کند. بنابراین باید آمادگی لازم برای بازیابی وضعیت برنامه در موقعیت‌های مختلف را داشته باشید.

بخوانید  کسب درآمد موبایلی: بررسی راه‌های مختلف تبلیغات در موبایل

سناریوی سوم: پراسس برنامه توسط سیستم‌عامل بسته می‌شود

تصویر کاملاً گویا است. توضیح دیگری لازم نیست.

مدیریت State

در هنگام kill شدن پراسس، وضعیت کامل stack ذخیره می‌شود ولی برای استفادۀ بهتر از منابع هر اکتیویتی درست زمانی که recreate می‌شود بازیابی می‌شود.

بخش سوم: استفاده از فرگمنت‌ها

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

سناریو اول: اکتیویتی همراه با فرگمنت شروع شده و پایان می‌یابد

متد onCreate در اکتیویتی به طور حتم قبل از onCreate فرگمنت اجرا می‌شود. ولی کال‌بک‌هایی مثل onStart و onResume که در بلوک‌های کنار هم در تصویر می‌بینید به صورت موازی اجرا می‌شوند. اینکه ترتیب اجرا به چه نحوی است را سیستم‌عامل تعیین می‌کند. مثلاً ممکن است onStart اکتیویتی پیش از onStart فرگمنت اجرا شود ولی در مرحلۀ بعد onResume فرگمنت سریع‌تر از onResume اکتیویتی اجرا شود.

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

مدیریت State

وضعیت فرگمنت دقیقاً همانند اکتیویتی ذخیره و بازیابی می‌شود؛ تنها فرقی که دارد این است که در فرگمنت onRestoreInstanceState وجود ندارد ولی شی Bundle در متدهای onCreate, onCreateView و onActivityCreated در دسترس آن هست. فرگمنت‌ها را می‌توان به صورت retain شده هم تعریف کرد؛ یعنی مقدار true را به متد setRetainInstance ارسال کنیم. در این حالت اگر تغییراتی در configuration به وجود آید، فرگمنت مثل اکتیویتی destroyed نمی‌شود. البته ویو destroyed می‌شود. در سناریوی بعدی خواهید دید که این موضوع کمی نمودار فرخوانی کال‌بک‌ها را تغییر می‌دهد.

سناریوی سوم: اکتیویتیِ حاوی فرگمنت چرخیده می‌شود

همانطور که می‌بینید در یک فرگمنت retain شده بعد از چرخاندن صفحه (تغییرات در کانفیگ) متد onDestroyed فرخوانی نمی‌شود چون دقیقاً از همان instance قبلی بعد از recreate شدن اکتیویتی برای فرگمنت جدید استفاده می‌شود. bundle همچنان در متد onActivityCreated در دسترس است. استفاده از فرگمنت‌های retain شده پیشنهاد نمی‌شود مگر زمانی که لازم بدانید در هنگام تغییر کانفیگ سیستم داده‌ها را بین فرگمنت‌ها پابرجا نگه دارید (برای فرگمنت‌های غیر UI). این همان چیزی است که کلاس ViewModel به صورت داخلی از آن استفاده می‌کند البته با یک API ساده تر.

ضمناً توجه داشته باشید که فرگمنت‌های back stack را نمی‌توان reatin تعریف کرد. یعنی در مورد این فرگمنت‌ها امکان استفاده از متد setRetainInstance با آرگومان true وجود ندارد. این متد برای زمانی کاربرد دارد که عملیات طولانی قرار است در یک فرگمنت انجام شود و نباید تغییرات در کانفیگ روی آن اثری داشته باشد.

سید ایوب کوکبی

نویسنده و مترجم...

مطالب مرتبط

0 دیدگاه

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *