مدیریت چرخه زندگی با اجزای مربوط به چرخه حیات بخشی از Android Jetpack .
مولفه های آگاه از چرخه حیات، اقداماتی را در پاسخ به تغییر وضعیت چرخه حیات یک جزء دیگر، مانند فعالیت ها و قطعات، انجام می دهند. این مولفه ها به شما کمک می کنند کدی با سازماندهی بهتر و اغلب سبک تر تولید کنید که نگهداری آن آسان تر است.
یک الگوی رایج این است که اقدامات اجزای وابسته را در روشهای چرخه حیات فعالیتها و قطعات پیادهسازی کنیم. با این حال، این الگو منجر به سازماندهی ضعیف کد و گسترش خطاها می شود. با استفاده از کامپوننتهای مربوط به چرخه حیات، میتوانید کد مولفههای وابسته را از روشهای چرخه حیات خارج کرده و به خود مؤلفهها منتقل کنید.
بسته androidx.lifecycle
کلاسها و رابطهایی را ارائه میکند که به شما امکان میدهد اجزای مربوط به چرخه زندگی را بسازید - که اجزایی هستند که میتوانند به طور خودکار رفتار خود را بر اساس وضعیت چرخه حیات فعلی یک فعالیت یا قطعه تنظیم کنند.
اکثر اجزای برنامه که در چارچوب Android تعریف شده اند دارای چرخه حیات هستند. چرخه های عمر توسط سیستم عامل یا کد فریمورک در حال اجرا در فرآیند شما مدیریت می شوند. آنها هسته اصلی نحوه عملکرد Android هستند و برنامه شما باید به آنها احترام بگذارد. عدم انجام این کار ممکن است باعث نشت حافظه یا حتی خرابی برنامه شود.
تصور کنید فعالیتی داریم که مکان دستگاه را روی صفحه نمایش می دهد. یک پیاده سازی رایج ممکن است مانند موارد زیر باشد:
کاتلین
internal class MyLocationListener( private val context: Context, private val callback: (Location) -> Unit ) { fun start() { // connect to system location service } fun stop() { // disconnect from system location service } } class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this) { location -> // update UI } } public override fun onStart() { super.onStart() myLocationListener.start() // manage other components that need to respond // to the activity lifecycle } public override fun onStop() { super.onStop() myLocationListener.stop() // manage other components that need to respond // to the activity lifecycle } }
جاوا
class MyLocationListener { public MyLocationListener(Context context, Callback callback) { // ... } void start() { // connect to system location service } void stop() { // disconnect from system location service } } class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; @Override public void onCreate(...) { myLocationListener = new MyLocationListener(this, (location) -> { // update UI }); } @Override public void onStart() { super.onStart(); myLocationListener.start(); // manage other components that need to respond // to the activity lifecycle } @Override public void onStop() { super.onStop(); myLocationListener.stop(); // manage other components that need to respond // to the activity lifecycle } }
حتی اگر این نمونه خوب به نظر می رسد، در یک برنامه واقعی، در نهایت تماس های زیادی دارید که رابط کاربری و سایر اجزا را در پاسخ به وضعیت فعلی چرخه حیات مدیریت می کنند. مدیریت چندین مؤلفه، مقدار قابل توجهی از کد را در متدهای چرخه حیات، مانند onStart()
و onStop()
قرار می دهد که نگهداری آنها را دشوار می کند.
علاوه بر این، هیچ تضمینی وجود ندارد که مؤلفه قبل از توقف فعالیت یا قطعه شروع شود. این امر به ویژه در صورتی صادق است که ما نیاز به انجام یک عملیات طولانی مدت، مانند بررسی تنظیمات در onStart()
. این می تواند شرایط مسابقه ای ایجاد کند که در آن متد onStop()
قبل از onStart()
به پایان می رسد و کامپوننت را بیشتر از زمان مورد نیاز زنده نگه می دارد.
کاتلین
class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this) { location -> // update UI } } public override fun onStart() { super.onStart() Util.checkUserStatus { result -> // what if this callback is invoked AFTER activity is stopped? if (result) { myLocationListener.start() } } } public override fun onStop() { super.onStop() myLocationListener.stop() } }
جاوا
class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, location -> { // update UI }); } @Override public void onStart() { super.onStart(); Util.checkUserStatus(result -> { // what if this callback is invoked AFTER activity is stopped? if (result) { myLocationListener.start(); } }); } @Override public void onStop() { super.onStop(); myLocationListener.stop(); } }
بسته androidx.lifecycle
کلاسها و رابطهایی را ارائه میکند که به شما کمک میکند این مشکلات را به روشی انعطافپذیر و ایزوله حل کنید.
چرخه زندگی
Lifecycle
کلاسی است که اطلاعات مربوط به وضعیت چرخه حیات یک جزء (مانند یک فعالیت یا یک قطعه) را در خود نگه می دارد و به اشیاء دیگر اجازه می دهد تا این حالت را مشاهده کنند.
Lifecycle
از دو شمارش اصلی برای ردیابی وضعیت چرخه حیات برای جزء مرتبط خود استفاده می کند:
- رویداد
- رویدادهای چرخه حیات که از چارچوب و کلاس
Lifecycle
ارسال می شوند. این رویدادها به رویدادهای برگشتی در فعالیت ها و قطعات نگاشت می شوند. - ایالت
- وضعیت فعلی جزء ردیابی شده توسط شی
Lifecycle
.
حالت ها را به عنوان گره های یک گراف و رویدادها را به عنوان لبه های بین این گره ها در نظر بگیرید.
یک کلاس می تواند با اجرای DefaultLifecycleObserver
و نادیده گرفتن متدهای مربوطه مانند onCreate
، onStart
و غیره، وضعیت چرخه حیات کامپوننت را نظارت کند. سپس می توانید با فراخوانی متد addObserver()
از کلاس Lifecycle
و ارسال نمونه ای از مشاهدهگر خود، همانطور که نشان داده شده است، یک مشاهدهگر اضافه کنید. در مثال زیر:
کاتلین
class MyObserver : DefaultLifecycleObserver { override fun onResume(owner: LifecycleOwner) { connect() } override fun onPause(owner: LifecycleOwner) { disconnect() } } myLifecycleOwner.getLifecycle().addObserver(MyObserver())
جاوا
public class MyObserver implements DefaultLifecycleObserver { @Override public void onResume(LifecycleOwner owner) { connect() } @Override public void onPause(LifecycleOwner owner) { disconnect() } } myLifecycleOwner.getLifecycle().addObserver(new MyObserver());
در مثال بالا، شی myLifecycleOwner
رابط LifecycleOwner
را پیاده سازی می کند که در قسمت زیر توضیح داده شده است.
Lifecycle Owner
LifecycleOwner
یک رابط متد واحد است که نشان می دهد کلاس دارای Lifecycle
است. این یک متد دارد، getLifecycle()
که باید توسط کلاس پیاده سازی شود. اگر به جای آن میخواهید چرخه حیات یک فرآیند برنامه کاربردی را مدیریت کنید، به ProcessLifecycleOwner
مراجعه کنید.
این رابط مالکیت یک Lifecycle
را از کلاسهای جداگانه، مانند Fragment
و AppCompatActivity
، انتزاع میکند و اجازه میدهد اجزایی را بنویسد که با آنها کار میکنند. هر کلاس برنامه سفارشی می تواند رابط LifecycleOwner
را پیاده سازی کند.
مؤلفههایی که DefaultLifecycleObserver
را پیادهسازی میکنند با مؤلفههایی که LifecycleOwner
را پیادهسازی میکنند یکپارچه کار میکنند، زیرا مالک میتواند چرخه حیاتی را ارائه دهد که ناظر میتواند آن را برای تماشا ثبت کند.
برای مثال ردیابی مکان، میتوانیم کلاس MyLocationListener
را وادار کنیم DefaultLifecycleObserver
پیادهسازی کند و سپس آن را با Lifecycle
فعالیت در متد onCreate()
مقداردهی کنیم. این به کلاس MyLocationListener
اجازه می دهد تا خودکفا باشد، به این معنی که منطق واکنش به تغییرات در وضعیت چرخه حیات به جای فعالیت در MyLocationListener
اعلام می شود. داشتن اجزای جداگانه ذخیره منطق خود، مدیریت فعالیت ها و منطق قطعات را آسان تر می کند.
کاتلین
class MyActivity : AppCompatActivity() { private lateinit var myLocationListener: MyLocationListener override fun onCreate(...) { myLocationListener = MyLocationListener(this, lifecycle) { location -> // update UI } Util.checkUserStatus { result -> if (result) { myLocationListener.enable() } } } }
جاوا
class MyActivity extends AppCompatActivity { private MyLocationListener myLocationListener; public void onCreate(...) { myLocationListener = new MyLocationListener(this, getLifecycle(), location -> { // update UI }); Util.checkUserStatus(result -> { if (result) { myLocationListener.enable(); } }); } }
یک مورد معمول استفاده این است که اگر Lifecycle
در حال حاضر وضعیت خوبی ندارد، از فراخوانی تماس های خاص اجتناب کنید. به عنوان مثال، اگر callback یک تراکنش قطعه را پس از ذخیره وضعیت فعالیت اجرا کند، باعث خرابی می شود، بنابراین ما هرگز نمی خواهیم آن تماس را فراخوانی کنیم.
برای آسان کردن این مورد، کلاس Lifecycle
به اشیاء دیگر اجازه می دهد تا وضعیت فعلی را جستجو کنند.
کاتلین
internal class MyLocationListener( private val context: Context, private val lifecycle: Lifecycle, private val callback: (Location) -> Unit ): DefaultLifecycleObserver { private var enabled = false override fun onStart(owner: LifecycleOwner) { if (enabled) { // connect } } fun enable() { enabled = true if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) { // connect if not connected } } override fun onStop(owner: LifecycleOwner) { // disconnect if connected } }
جاوا
class MyLocationListener implements DefaultLifecycleObserver { private boolean enabled = false; public MyLocationListener(Context context, Lifecycle lifecycle, Callback callback) { ... } @Override public void onStart(LifecycleOwner owner) { if (enabled) { // connect } } public void enable() { enabled = true; if (lifecycle.getCurrentState().isAtLeast(STARTED)) { // connect if not connected } } @Override public void onStop(LifecycleOwner owner) { // disconnect if connected } }
با این پیاده سازی، کلاس LocationListener
ما کاملاً از چرخه حیات آگاه است. اگر لازم است از LocationListener
خود از یک اکتیویتی یا قطعه دیگر استفاده کنیم، فقط باید آن را مقداردهی اولیه کنیم. تمام عملیات راه اندازی و حذف توسط خود کلاس مدیریت می شود.
اگر کتابخانهای کلاسهایی را ارائه میدهد که باید با چرخه حیات Android کار کنند، توصیه میکنیم از مؤلفههای مربوط به چرخه حیات استفاده کنید. مشتریان کتابخانه شما می توانند به راحتی آن اجزا را بدون مدیریت چرخه عمر دستی در سمت مشتری یکپارچه کنند.
پیاده سازی LifecycleOwner سفارشی
Fragments and Activities in Support Library نسخه 26.1.0 و نسخه های جدیدتر از قبل رابط LifecycleOwner
را پیاده سازی کرده است.
اگر یک کلاس سفارشی دارید که میخواهید LifecycleOwner
بسازید، میتوانید از کلاس LifecycleRegistry استفاده کنید، اما باید رویدادها را به آن کلاس ارسال کنید، همانطور که در مثال کد زیر نشان داده شده است:
کاتلین
class MyActivity : Activity(), LifecycleOwner { private lateinit var lifecycleRegistry: LifecycleRegistry override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) lifecycleRegistry = LifecycleRegistry(this) lifecycleRegistry.markState(Lifecycle.State.CREATED) } public override fun onStart() { super.onStart() lifecycleRegistry.markState(Lifecycle.State.STARTED) } override fun getLifecycle(): Lifecycle { return lifecycleRegistry } }
جاوا
public class MyActivity extends Activity implements LifecycleOwner { private LifecycleRegistry lifecycleRegistry; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); lifecycleRegistry = new LifecycleRegistry(this); lifecycleRegistry.markState(Lifecycle.State.CREATED); } @Override public void onStart() { super.onStart(); lifecycleRegistry.markState(Lifecycle.State.STARTED); } @NonNull @Override public Lifecycle getLifecycle() { return lifecycleRegistry; } }
بهترین روش ها برای اجزای آگاه از چرخه حیات
- کنترلکنندههای رابط کاربری (فعالیتها و قطعات) خود را تا حد امکان نازک نگه دارید. آنها نباید سعی کنند داده های خود را به دست آورند. در عوض، از یک
ViewModel
برای انجام این کار استفاده کنید، و یک شیLiveData
را مشاهده کنید تا تغییرات را به نماها منعکس کند. - سعی کنید UIهای مبتنی بر داده بنویسید که در آن مسئولیت کنترلر UI شما این است که نماها را با تغییر داده ها به روز کند یا اقدامات کاربر را به
ViewModel
اطلاع دهد. - منطق داده های خود را در کلاس
ViewModel
خود قرار دهید.ViewModel
باید به عنوان رابط بین کنترلر UI و بقیه برنامه شما عمل کند. اما مراقب باشید، وظیفهViewModel
نیست که داده ها را واکشی کند (مثلاً از یک شبکه). در عوض،ViewModel
باید مؤلفه مناسب را برای واکشی داده ها فراخوانی کند، سپس نتیجه را به کنترلر UI ارائه دهد. - از Data Binding برای حفظ یک رابط تمیز بین نماهای خود و کنترلر UI استفاده کنید. این به شما امکان می دهد تا دیدگاه های خود را شفاف تر کنید و کد به روز رسانی مورد نیاز برای نوشتن در فعالیت ها و قطعات خود را به حداقل برسانید. اگر ترجیح می دهید این کار را در زبان برنامه نویسی جاوا انجام دهید، از کتابخانه ای مانند Butter Knife استفاده کنید تا از کدهای boilerplate اجتناب کنید و انتزاع بهتری داشته باشید.
- اگر UI شما پیچیده است، ایجاد یک کلاس ارائه کننده برای مدیریت تغییرات UI را در نظر بگیرید. ممکن است این یک کار پر زحمت باشد، اما میتواند آزمایش اجزای رابط کاربری شما را آسانتر کند.
- از ارجاع به یک
View
یا زمینهActivity
درViewModel
خود اجتناب کنید. اگرViewModel
بیشتر از فعالیت خود ادامه دهد (در صورت تغییر پیکربندی)، فعالیت شما نشت می کند و به درستی توسط زباله جمع کن دفع نمی شود. - از کوروتین های Kotlin برای مدیریت کارهای طولانی مدت و سایر عملیاتی که می توانند به صورت ناهمزمان اجرا شوند استفاده کنید.
از کیس برای اجزای آگاه از چرخه حیات استفاده کنید
مولفه های آگاه از چرخه حیات می توانند مدیریت چرخه عمر را در موارد مختلف برای شما آسان تر کنند. چند نمونه عبارتند از:
- جابهجایی بین بهروزرسانیهای مکان درشت و ریز. برای فعال کردن بهروزرسانیهای دقیق مکان در زمانی که برنامه موقعیت مکانی شما قابل مشاهده است، از مؤلفههای مربوط به چرخه حیات استفاده کنید و وقتی برنامه در پسزمینه است، بهروزرسانیهای درشت دانه را تغییر دهید.
LiveData
، یک مؤلفه آگاه از چرخه حیات، به برنامه شما اجازه می دهد تا زمانی که کاربر مکان خود را تغییر می دهد، به طور خودکار رابط کاربری را به روز کند. - توقف و شروع بافر ویدیو. برای شروع بافر کردن ویدیو در اسرع وقت از اجزای مربوط به چرخه حیات استفاده کنید، اما پخش را تا شروع کامل برنامه به تعویق بیندازید. همچنین میتوانید از مؤلفههای مربوط به چرخه حیات برای پایان دادن به بافر زمانی که برنامهتان از بین میرود، استفاده کنید.
- شروع و توقف اتصال شبکه برای فعال کردن بهروزرسانی زنده (جریانگذاری) دادههای شبکه در حالی که برنامه در پیشزمینه است و همچنین برای توقف خودکار زمانی که برنامه به پسزمینه میرود، از مؤلفههای آگاه از چرخه حیات استفاده کنید.
- مکث و ازسرگیری طرحهای متحرک. از مؤلفههای آگاه از چرخه حیات برای رسیدگی به ترسیمهای متحرک در حالت مکث زمانی که برنامه در پسزمینه است، استفاده کنید و پس از قرار گرفتن برنامه در پیشزمینه، طرحها را از سر بگیرید.
رسیدگی به رویدادهای توقف
هنگامی که Lifecycle
به AppCompatActivity
یا Fragment
تعلق دارد، وضعیت Lifecycle
به CREATED
تغییر میکند و با فراخوانی onSaveInstanceState()
AppCompatActivity
یا Fragment
، رویداد ON_STOP
ارسال میشود.
وقتی وضعیت یک Fragment
یا AppCompatActivity
از طریق onSaveInstanceState()
ذخیره می شود، تا زمانی که ON_START
فراخوانی نشود، رابط کاربری آن تغییرناپذیر در نظر گرفته می شود. تلاش برای تغییر رابط کاربری پس از ذخیره حالت ممکن است باعث ناهماهنگی در وضعیت پیمایش برنامه شما شود، به همین دلیل است که اگر برنامه پس از ذخیره وضعیت، FragmentTransaction
اجرا کند FragmentManager
یک استثنا ایجاد می کند. برای جزئیات بیشتر به commit()
مراجعه کنید.
LiveData
با خودداری از فراخوانی ناظر خود در صورتی که Lifecycle
مرتبط ناظر حداقل STARTED
نشده باشد، از این حاشیه خارج از جعبه جلوگیری می کند. در پشت صحنه، isAtLeast()
قبل از تصمیم به فراخوانی ناظر خود فراخوانی می کند.
متأسفانه، متد onStop()
AppCompatActivity
پس از onSaveInstanceState()
فراخوانی می شود که در آن شکافی ایجاد می شود که در آن تغییرات حالت رابط کاربری مجاز نیست اما Lifecycle
هنوز به حالت CREATED
منتقل نشده است.
برای جلوگیری از این مشکل، کلاس Lifecycle
در نسخه beta2
و پایینتر، بدون ارسال رویداد، وضعیت را بهعنوان CREATED
علامتگذاری میکند تا هر کدی که وضعیت فعلی را بررسی میکند، مقدار واقعی را دریافت کند، حتی اگر رویداد تا زمانی که onStop()
فراخوانی نشود، مقدار واقعی را دریافت کند. سیستم
متاسفانه این راه حل دو مشکل عمده دارد:
- در سطح API 23 و پایین تر، سیستم اندروید در واقع وضعیت یک فعالیت را حتی اگر تا حدی تحت پوشش فعالیت دیگری باشد ذخیره می کند. به عبارت دیگر، سیستم اندروید
onSaveInstanceState()
را فراخوانی می کند اما لزوماًonStop()
فراخوانی نمی کند. این یک فاصله بالقوه طولانی ایجاد می کند که در آن ناظر همچنان فکر می کند که چرخه حیات فعال است، حتی اگر حالت رابط کاربری آن قابل تغییر نباشد. - هر کلاسی که بخواهد رفتاری مشابه با کلاس
LiveData
نشان دهد باید راه حل ارائه شده توسطLifecycle
نسخهbeta 2
و پایین تر را پیاده سازی کند.
منابع اضافی
برای کسب اطلاعات بیشتر در مورد مدیریت چرخه عمر با اجزای مربوط به چرخه حیات، به منابع اضافی زیر مراجعه کنید.
نمونه ها
- نمونه اولیه اجزای معماری اندروید
- Sunflower ، یک برنامه آزمایشی که بهترین روشها را با اجزای معماری نشان میدهد
Codelabs
وبلاگ ها
{% کلمه به کلمه %}برای شما توصیه می شود
- توجه: وقتی جاوا اسکریپت خاموش است، متن پیوند نمایش داده می شود
- نمای کلی LiveData
- از کوروتین های کاتلین با اجزای آگاه از چرخه حیات استفاده کنید
- ماژول حالت ذخیره شده برای ViewModel