مفاهیم و پیادهسازی Jetpack Compose
همانطور که یک کاربر در برنامه شما حرکت میکند، از آن خارج میشود و به آن برمیگردد، نمونههای Activity در برنامه شما در چرخه حیات خود از حالتهای مختلفی عبور میکنند. کلاس Activity تعدادی callback ارائه میدهد که به activity اطلاع میدهد چه زمانی یک حالت تغییر میکند یا اینکه سیستم در حال ایجاد، توقف یا از سرگیری یک activity یا از بین بردن فرآیندی است که activity در آن قرار دارد.
در داخل متدهای فراخوانی چرخه حیات، میتوانید نحوه رفتار اکتیویتی خود را هنگام خروج و ورود مجدد کاربر به اکتیویتی، مشخص کنید. برای مثال، اگر در حال ساخت یک پخشکننده ویدیوی استریمینگ هستید، میتوانید ویدیو را متوقف کرده و اتصال شبکه را هنگامی که کاربر به برنامه دیگری میرود، قطع کنید. هنگامی که کاربر برمیگردد، میتوانید دوباره به شبکه متصل شوید و به کاربر اجازه دهید ویدیو را از همان نقطه از سر بگیرد.
هر فراخوانی برگشتی به شما امکان میدهد کار خاصی را انجام دهید که مناسب با تغییر وضعیت داده شده است. انجام کار درست در زمان مناسب و مدیریت صحیح انتقالها، برنامه شما را قویتر و کارآمدتر میکند. به عنوان مثال، پیادهسازی خوب فراخوانیهای برگشتی چرخه عمر میتواند به برنامه شما کمک کند تا از موارد زیر جلوگیری کند:
- اگر کاربر هنگام استفاده از برنامه شما تماس تلفنی دریافت کند یا به برنامه دیگری برود، برنامه از کار میافتد.
- مصرف منابع ارزشمند سیستم در زمانی که کاربر به طور فعال از آن استفاده نمیکند.
- از دست دادن پیشرفت کاربر در صورت ترک برنامه و بازگشت مجدد به آن در زمان دیگر.
- از کار افتادن یا از دست دادن پیشرفت کاربر هنگام چرخش صفحه بین حالت افقی و عمودی.
این سند چرخه حیات فعالیت را با جزئیات توضیح میدهد. این سند با توصیف الگوی چرخه حیات شروع میشود. سپس، هر یک از فراخوانیهای برگشتی را توضیح میدهد: چه اتفاقی در داخل هنگام اجرای آنها میافتد و چه چیزی را باید در طول آنها پیادهسازی کنید.
سپس به طور خلاصه رابطه بین وضعیت فعالیت و آسیبپذیری یک فرآیند در برابر از کار افتادن توسط سیستم را معرفی میکند. در نهایت، چندین موضوع مرتبط با انتقال بین وضعیتهای فعالیت را مورد بحث قرار میدهد.
برای کسب اطلاعات در مورد مدیریت چرخههای حیات، از جمله راهنمایی در مورد بهترین شیوهها، به مدیریت چرخههای حیات با اجزای آگاه از چرخه حیات و ذخیره حالتهای رابط کاربری مراجعه کنید. برای یادگیری نحوه معماری یک برنامه قوی و با کیفیت تولید با استفاده از فعالیتها در ترکیب با اجزای معماری، به راهنمای معماری برنامه مراجعه کنید.
مفاهیم چرخه حیات فعالیت
برای پیمایش گذارها بین مراحل چرخه حیات فعالیت، کلاس Activity مجموعهای اصلی از شش فراخوانی را ارائه میدهد: onCreate ، onStart ، onResume ، onPause ، onStop و onDestroy . سیستم هر یک از این فراخوانیها را با ورود فعالیت به حالت جدید فراخوانی میکند.
شکل ۱، نمایش بصری این الگو را نشان میدهد.

به محض اینکه کاربر شروع به ترک اکتیویتی میکند، سیستم متدهایی را برای از بین بردن اکتیویتی فراخوانی میکند. در برخی موارد، اکتیویتی فقط تا حدی از بین میرود و هنوز در حافظه قرار دارد، مانند زمانی که کاربر به برنامه دیگری سوئیچ میکند. در این موارد، اکتیویتی هنوز میتواند به پیشزمینه بازگردد.
اگر کاربر به فعالیت بازگردد، از جایی که کاربر آن را رها کرده بود، از سر گرفته میشود. به جز چند مورد استثنا، برنامهها هنگام اجرا در پسزمینه، اجازه شروع فعالیتها را ندارند .
احتمال اینکه سیستم یک فرآیند مشخص را به همراه فعالیتهای درون آن از بین ببرد، به وضعیت فعالیت در آن زمان بستگی دارد. برای اطلاعات بیشتر در مورد رابطه بین وضعیت و آسیبپذیری در برابر حذف، به بخش مربوط به وضعیت فعالیت و حذف از حافظه مراجعه کنید.
بسته به پیچیدگی فعالیت شما، احتمالاً نیازی به پیادهسازی همه متدهای چرخه حیات ندارید. با این حال، مهم است که هر یک را درک کنید و آنهایی را پیادهسازی کنید که باعث میشوند برنامه شما مطابق انتظار کاربران رفتار کند.
فراخوانیهای چرخه عمر
این بخش اطلاعات مفهومی و پیادهسازی در مورد متدهای فراخوانی مورد استفاده در طول چرخه حیات فعالیت را ارائه میدهد.
برخی از اقدامات به متدهای چرخه حیات فعالیت تعلق دارند. با این حال، کدی را قرار دهید که اقدامات یک کامپوننت وابسته را در کامپوننت پیادهسازی میکند، نه در متد چرخه حیات فعالیت. برای دستیابی به این هدف، باید کامپوننت وابسته را از چرخه حیات آگاه کنید. برای یادگیری نحوه آگاه کردن کامپوننتهای وابسته از چرخه حیات، به بخش مدیریت چرخه حیات با کامپوننتهای آگاه از چرخه حیات مراجعه کنید.
روی ایجاد
شما باید این تابع فراخوانی (callback) را پیادهسازی کنید، که وقتی سیستم برای اولین بار activity را ایجاد میکند، اجرا میشود. در هنگام ایجاد activity، activity وارد حالت Created میشود. در متد onCreate ، منطق اولیه راهاندازی برنامه را که فقط یک بار در کل طول عمر activity اتفاق میافتد، اجرا کنید.
برای مثال، پیادهسازی شما از onCreate ممکن است دادهها را به لیستها متصل کند، activity را با یک ViewModel مرتبط کند و برخی از متغیرهای کلاس-اسکوپ را نمونهسازی کند. این متد پارامتر savedInstanceState را دریافت میکند که یک شیء Bundle است که شامل وضعیت ذخیره شده قبلی activity است. اگر activity قبلاً هرگز وجود نداشته باشد، مقدار شیء Bundle تهی (null) است.
اگر یک کامپوننت آگاه از چرخه حیات دارید که به چرخه حیات اکتیویتی شما متصل است، رویداد ON_CREATE را دریافت میکند. متدی که با @OnLifecycleEvent حاشیهنویسی شده است، فراخوانی میشود تا کامپوننت آگاه از چرخه حیات شما بتواند هر کد راهاندازی مورد نیاز برای حالت ایجاد شده را انجام دهد.
The following example of the onCreate method shows fundamental setup for the activity, such as declaring the user interface (defined in an XML layout file), defining member variables, and configuring some of the UI. In this example, the XML layout file passes the file's resource ID R.layout.main_activity to setContentView .
کاتلین
lateinit var textView: TextView
// Some transient state for the activity instance.
var gameState: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
// Call the superclass onCreate to complete the creation of
// the activity, like the view hierarchy.
super.onCreate(savedInstanceState)
// Recover the instance state.
gameState = savedInstanceState?.getString(GAME_STATE_KEY)
// Set the user interface layout for this activity.
// The layout is defined in the project res/layout/main_activity.xml file.
setContentView(R.layout.main_activity)
// Initialize member TextView so it is available later.
textView = findViewById(R.id.text_view)
}
// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
textView.text = savedInstanceState?.getString(TEXT_VIEW_KEY)
}
// Invoked when the activity might be temporarily destroyed; save the instance state here.
override fun onSaveInstanceState(outState: Bundle?) {
outState?.run {
putString(GAME_STATE_KEY, gameState)
putString(TEXT_VIEW_KEY, textView.text.toString())
}
// Call superclass to save any view hierarchy.
super.onSaveInstanceState(outState)
}
جاوا
TextView textView;
// Some transient state for the activity instance.
String gameState;
@Override
public void onCreate(Bundle savedInstanceState) {
// Call the superclass onCreate to complete the creation of
// the activity, like the view hierarchy.
super.onCreate(savedInstanceState);
// Recover the instance state.
if (savedInstanceState != null) {
gameState = savedInstanceState.getString(GAME_STATE_KEY);
}
// Set the user interface layout for this activity.
// The layout is defined in the project res/layout/main_activity.xml file.
setContentView(R.layout.main_activity);
// Initialize member TextView so it is available later.
textView = (TextView) findViewById(R.id.text_view);
}
// This callback is called only when there is a saved instance previously saved using
// onSaveInstanceState(). Some state is restored in onCreate(). Other state can optionally
// be restored here, possibly usable after onStart() has completed.
// The savedInstanceState Bundle is same as the one used in onCreate().
@Override
public void onRestoreInstanceState(Bundle savedInstanceState) {
textView.setText(savedInstanceState.getString(TEXT_VIEW_KEY));
}
// Invoked when the activity might be temporarily destroyed; save the instance state here.
@Override
public void onSaveInstanceState(Bundle outState) {
outState.putString(GAME_STATE_KEY, gameState);
outState.putString(TEXT_VIEW_KEY, textView.getText());
// Call superclass to save any view hierarchy.
super.onSaveInstanceState(outState);
}
به عنوان جایگزینی برای تعریف فایل XML و ارسال آن به setContentView ، میتوانید اشیاء View جدیدی را در کد activity خود ایجاد کنید و با قرار دادن اشیاء View جدید در یک ViewGroup ، یک سلسله مراتب view ایجاد کنید. سپس با ارسال ViewGroup ریشه به setContentView ، از آن طرحبندی استفاده میکنید. برای اطلاعات بیشتر در مورد ایجاد رابط کاربری، به مستندات رابط کاربری مراجعه کنید.
اکتیویتی شما در حالت Created باقی نمیماند. پس از اتمام اجرای متد onCreate ، اکتیویتی وارد حالت Started میشود و سیستم متدهای onStart و onResume را پشت سر هم فراخوانی میکند.
شروع
وقتی اکتیویتی وارد حالت شروع میشود، سیستم onStart را فراخوانی میکند. این فراخوانی، اکتیویتی را برای کاربر قابل مشاهده میکند، زیرا برنامه برای ورود اکتیویتی به پیشزمینه و تعاملی شدن آماده میشود. برای مثال، این متد جایی است که کدی که رابط کاربری را حفظ میکند، مقداردهی اولیه میشود.
When the activity moves to the Started state, any lifecycle-aware component tied to the activity's lifecycle receives the ON_START event.
The onStart method completes quickly and, as with the Created state, the activity does not remain in the Started state. Once this callback finishes, the activity enters the Resumed state and the system invokes the onResume method.
در رزومه
وقتی اکتیویتی وارد حالت Resume میشود، به پیشزمینه میآید و سیستم فراخوانی onResume را فراخوانی میکند. این حالتی است که برنامه در آن با کاربر تعامل میکند. برنامه در این حالت باقی میماند تا زمانی که اتفاقی بیفتد که تمرکز را از برنامه خارج کند، مانند دریافت تماس تلفنی توسط دستگاه، رفتن کاربر به اکتیویتی دیگر یا خاموش شدن صفحه دستگاه.
وقتی اکتیویتی به حالت Resume میرود، هر کامپوننت آگاه از چرخه حیات که به چرخه حیات اکتیویتی گره خورده است، رویداد ON_RESUME را دریافت میکند. این جایی است که کامپوننتهای چرخه حیات میتوانند هر عملکردی را که نیاز به اجرا دارد در حالی که کامپوننت قابل مشاهده و در پیشزمینه است، مانند شروع پیشنمایش دوربین، فعال کنند.
وقتی یک رویداد وقفهدار رخ میدهد، فعالیت وارد حالت Paused میشود و سیستم فراخوانی onPause را فراخوانی میکند.
اگر فعالیت از حالت Paused به حالت Resume بازگردد، سیستم بار دیگر متد onResume را فراخوانی میکند. به همین دلیل، onResume را برای مقداردهی اولیه کامپوننتهایی که در طول onPause رها میکنید و برای انجام هرگونه مقداردهی اولیه دیگری که باید هر بار که فعالیت وارد حالت Resume میشود، رخ دهد، پیادهسازی کنید.
در اینجا مثالی از یک کامپوننت آگاه از چرخه حیات (lifecycle-aware) آورده شده است که وقتی کامپوننت رویداد ON_RESUME دریافت میکند، به دوربین دسترسی پیدا میکند:
کاتلین
class CameraComponent : LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun initializeCamera() {
if (camera == null) {
getCamera()
}
}
...
}
جاوا
public class CameraComponent implements LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
public void initializeCamera() {
if (camera == null) {
getCamera();
}
}
...
}
The preceding code initializes the camera once the LifecycleObserver receives the ON_RESUME event. In multi-window mode, however, your activity might be fully visible even when it is in the Paused state. For example, when the app is in multi-window mode and the user taps the window that does not contain your activity, your activity moves to the Paused state.
اگر میخواهید دوربین فقط زمانی فعال باشد که برنامه Resume شده باشد (در پیشزمینه قابل مشاهده و فعال باشد)، دوربین را پس از رویداد ON_RESUME که قبلاً توضیح داده شد، مقداردهی اولیه کنید. اگر میخواهید دوربین را در حالی که فعالیت متوقف شده اما قابل مشاهده است، مانند حالت چند پنجرهای، فعال نگه دارید، دوربین را پس از رویداد ON_START مقداردهی اولیه کنید.
با این حال، فعال بودن دوربین در حالی که فعالیت شما متوقف شده است، ممکن است دسترسی به دوربین را برای برنامهی از سر گرفته شدهی دیگری در حالت چند پنجرهای مسدود کند. گاهی اوقات لازم است که دوربین را در حالی که فعالیت شما متوقف شده است، فعال نگه دارید، اما اگر این کار را انجام دهید، ممکن است تجربهی کلی کاربر را خراب کند.
به همین دلیل، با دقت فکر کنید که در کجای چرخه حیات، مناسبترین زمان برای کنترل منابع مشترک سیستم در زمینه حالت چند پنجرهای است. برای کسب اطلاعات بیشتر در مورد پشتیبانی از حالت چند پنجرهای، به بخش «پشتیبانی از حالت چند پنجرهای» مراجعه کنید.
صرف نظر از اینکه کدام رویداد build-up را برای انجام عملیات مقداردهی اولیه انتخاب میکنید، مطمئن شوید که از رویداد چرخه عمر مربوطه برای آزادسازی منبع استفاده میکنید. اگر چیزی را پس از رویداد ON_START مقداردهی اولیه میکنید، آن را پس از رویداد ON_STOP رها یا خاتمه دهید. اگر پس از رویداد ON_RESUME مقداردهی اولیه میکنید، پس از رویداد ON_PAUSE رها کنید.
قطعه کد قبلی، کد مقداردهی اولیه دوربین را در یک کامپوننت آگاه از چرخه حیات قرار میدهد. در عوض میتوانید این کد را مستقیماً در فراخوانیهای چرخه حیات فعالیت، مانند onStart و onStop قرار دهید، اما ما این را توصیه نمیکنیم. افزودن این منطق به یک کامپوننت مستقل و آگاه از چرخه حیات به شما امکان میدهد بدون نیاز به کپی کردن کد، از کامپوننت در چندین فعالیت دوباره استفاده کنید. برای یادگیری نحوه ایجاد یک کامپوننت آگاه از چرخه حیات، به مدیریت چرخههای حیات با کامپوننتهای آگاه از چرخه حیات (Views) مراجعه کنید.
onPause
سیستم این متد را به عنوان اولین نشانه مبنی بر خروج کاربر از اکتیویتی شما فراخوانی میکند، اگرچه این همیشه به معنای از بین رفتن اکتیویتی نیست. این متد نشان میدهد که اکتیویتی دیگر در پیشزمینه نیست، اما اگر کاربر در حالت چند پنجرهای باشد، همچنان قابل مشاهده است. دلایل مختلفی وجود دارد که چرا یک اکتیویتی ممکن است وارد این حالت شود:
- رویدادی که اجرای برنامه را متوقف میکند، همانطور که در بخش مربوط به فراخوانی
onResumeتوضیح داده شد، فعالیت فعلی را متوقف میکند. این رایجترین مورد است. - در حالت چند پنجرهای، در هر زمان فقط یک برنامه فوکوس دارد و سیستم تمام برنامههای دیگر را متوقف میکند.
- باز کردن یک اکتیویتی جدید و نیمهشفاف، مانند یک پنجرهی محاورهای، اکتیویتیای که آن را پوشش میدهد را متوقف میکند. تا زمانی که اکتیویتی تا حدی قابل مشاهده باشد اما در فوکوس نباشد، در حالت مکث باقی میماند.
وقتی یک اکتیویتی به حالت Paused میرود، هر کامپوننت آگاه از چرخه حیات که به چرخه حیات اکتیویتی گره خورده است، رویداد ON_PAUSE را دریافت میکند. در این حالت، کامپوننتهای چرخه حیات میتوانند هر عملکردی را که نیازی به اجرا ندارد، در حالی که کامپوننت در پیشزمینه نیست، متوقف کنند، مانند متوقف کردن پیشنمایش دوربین.
از متد onPause برای مکث یا تنظیم عملیاتی که نمیتوانند ادامه یابند، یا ممکن است در حالت تعلیق ادامه یابند، در حالی که Activity در حالت مکث است، و انتظار دارید که به زودی از سر گرفته شوند، استفاده کنید.
همچنین میتوانید از متد onPause برای آزاد کردن منابع سیستم، هندلهای مربوط به حسگرها (مانند GPS) یا هر منبعی که بر عمر باتری تأثیر میگذارد، در زمانی که فعالیت شما متوقف شده است و کاربر به آنها نیازی ندارد، استفاده کنید.
با این حال، همانطور که در بخش مربوط به onResume ذکر شد، اگر برنامه در حالت چند پنجرهای باشد، یک فعالیت متوقف شده ممکن است هنوز کاملاً قابل مشاهده باشد. برای آزادسازی کامل یا تنظیم منابع و عملیات مرتبط با رابط کاربری برای پشتیبانی بهتر از حالت چند پنجرهای، استفاده از onStop را به جای onPause در نظر بگیرید.
مثال زیر از یک LifecycleObserver که به رویداد ON_PAUSE واکنش نشان میدهد، نقطه مقابل مثال قبلی رویداد ON_RESUME است که دوربینی را که پس از دریافت رویداد ON_RESUME مقداردهی اولیه میشود، آزاد میکند:
کاتلین
class CameraComponent : LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun releaseCamera() {
camera?.release()
camera = null
}
...
}
جاوا
public class JavaCameraComponent implements LifecycleObserver {
...
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
public void releaseCamera() {
if (camera != null) {
camera.release();
camera = null;
}
}
...
}
این مثال کد مربوط به رهاسازی دوربین را پس از دریافت رویداد ON_PAUSE توسط LifecycleObserver قرار میدهد.
اجرای onPause بسیار کوتاه است و لزوماً زمان کافی برای انجام عملیات ذخیره را ارائه نمیدهد. به همین دلیل، onPause برای ذخیره دادههای برنامه یا کاربر، برقراری تماسهای شبکه یا اجرای تراکنشهای پایگاه داده استفاده نکنید . چنین کارهایی ممکن است قبل از اتمام متد، کامل نشوند.
در عوض، عملیات خاموش کردن با بار سنگین را در طول onStop انجام دهید. برای اطلاعات بیشتر در مورد عملیات مناسب برای انجام در طول onStop ، به بخش بعدی مراجعه کنید. برای اطلاعات بیشتر در مورد ذخیره دادهها، به بخش مربوط به ذخیره و بازیابی وضعیت مراجعه کنید.
تکمیل متد onPause به این معنی نیست که اکتیویتی از حالت Paused خارج میشود. بلکه، اکتیویتی در این حالت باقی میماند تا زمانی که یا اکتیویتی از سر گرفته شود یا کاملاً برای کاربر نامرئی شود. اگر اکتیویتی از سر گرفته شود، سیستم بار دیگر فراخوانی onResume را فراخوانی میکند.
اگر اکتیویتی از حالت Paused به حالت Resume بازگردد، سیستم نمونه Activity را در حافظه نگه میدارد و هنگامی که سیستم onResume را فراخوانی میکند، آن نمونه را فراخوانی میکند. در این سناریو، نیازی به مقداردهی مجدد اجزای ایجاد شده در طول هر یک از متدهای callback منتهی به حالت Resume ندارید. اگر اکتیویتی کاملاً نامرئی شود، سیستم onStop را فراخوانی میکند.
آناستاپ
وقتی اکتیویتی شما دیگر برای کاربر قابل مشاهده نباشد، وارد حالت متوقف شده (Stopped ) میشود و سیستم تابع onStop را فراخوانی میکند. این اتفاق زمانی رخ میدهد که یک اکتیویتی تازه اجرا شده کل صفحه را بپوشاند. سیستم همچنین وقتی اجرای اکتیویتی تمام میشود و در شرف خاتمه یافتن است، تابع onStop را فراخوانی میکند.
وقتی اکتیویتی به حالت متوقفشده (Stopped) میرود، هر کامپوننت آگاه از چرخه حیات که به چرخه حیات اکتیویتی گره خورده است، رویداد ON_STOP را دریافت میکند. در این حالت، کامپوننتهای چرخه حیات میتوانند هر عملکردی را که نیازی به اجرا ندارد، در حالی که کامپوننت روی صفحه نمایش قابل مشاهده نیست، متوقف کنند.
در روش onStop ، منابعی را که مورد نیاز نیستند، در حالی که برنامه برای کاربر قابل مشاهده نیست، آزاد یا تنظیم کنید. به عنوان مثال، برنامه شما ممکن است انیمیشنها را متوقف کند یا از بهروزرسانیهای موقعیت مکانی ریزدانه به درشتدانه تغییر کند. استفاده از onStop به جای onPause به این معنی است که کار مرتبط با رابط کاربری ادامه مییابد، حتی زمانی که کاربر در حال مشاهده فعالیت شما در حالت چند پنجرهای است.
همچنین، onStop برای انجام عملیات خاموش کردن نسبتاً فشرده CPU استفاده کنید. برای مثال، اگر نمیتوانید زمان بهتری برای ذخیره اطلاعات در پایگاه داده پیدا کنید، میتوانید این کار را در طول onStop انجام دهید. مثال زیر پیادهسازی onStop را نشان میدهد که محتویات یک یادداشت پیشنویس را در حافظه دائمی ذخیره میکند:
کاتلین
override fun onStop() {
// Call the superclass method first.
super.onStop()
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
val values = ContentValues().apply {
put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText())
put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle())
}
// Do this update in background on an AsyncQueryHandler or equivalent.
asyncQueryHandler.startUpdate(
token, // int token to correlate calls
null, // cookie, not used here
uri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
)
}
جاوا
@Override
protected void onStop() {
// Call the superclass method first.
super.onStop();
// Save the note's current draft, because the activity is stopping
// and we want to be sure the current note progress isn't lost.
ContentValues values = new ContentValues();
values.put(NotePad.Notes.COLUMN_NAME_NOTE, getCurrentNoteText());
values.put(NotePad.Notes.COLUMN_NAME_TITLE, getCurrentNoteTitle());
// Do this update in background on an AsyncQueryHandler or equivalent.
asyncQueryHandler.startUpdate (
mToken, // int token to correlate calls
null, // cookie, not used here
uri, // The URI for the note to update.
values, // The map of column names and new values to apply to them.
null, // No SELECT criteria are used.
null // No WHERE columns are used.
);
}
نمونه کد قبلی مستقیماً از SQLite استفاده میکند. با این حال، ما استفاده از Room، یک کتابخانه persistence که یک لایه انتزاعی روی SQLite فراهم میکند، را توصیه میکنیم. برای کسب اطلاعات بیشتر در مورد مزایای استفاده از Room و نحوه پیادهسازی Room در برنامه خود، به راهنمای کتابخانه persistence Room مراجعه کنید.
When your activity enters the Stopped state, the Activity object is kept resident in memory: it maintains all state and member information, but is not attached to the window manager. When the activity resumes, it recalls this information.
نیازی نیست کامپوننتهای ایجاد شده در طول هیچ یک از متدهای فراخوانی (callback) منتهی به حالت Resume را دوباره مقداردهی اولیه کنید. سیستم همچنین وضعیت فعلی هر شیء View را در طرحبندی پیگیری میکند، بنابراین اگر کاربر متنی را در یک ویجت EditText وارد کند، آن محتوا حفظ میشود، بنابراین نیازی به ذخیره و بازیابی آن ندارید.
از حالت متوقفشده، اکتیویتی یا برای تعامل با کاربر برمیگردد، یا اجرای آن تمام شده و از بین میرود. اگر اکتیویتی برگردد، سیستم onRestart را فراخوانی میکند. اگر اجرای Activity تمام شده باشد، سیستم onDestroy را فراخوانی میکند.
روی تخریب
متد onDestroy قبل از اینکه اکتیویتی از بین برود، فراخوانی میشود. سیستم این فراخوانی را به یکی از دو دلیل زیر فراخوانی میکند:
- فعالیت در حال اتمام است، به این دلیل که کاربر به طور کامل فعالیت را رد کرده است یا به این دلیل که فراخوانی فعالیت
finish. - سیستم به دلیل تغییر پیکربندی، مانند چرخش دستگاه یا ورود به حالت چند پنجرهای، موقتاً فعالیت را از بین میبرد.
وقتی اکتیویتی به حالت تخریبشده میرود، هر کامپوننت آگاه از چرخه حیات که به چرخه حیات اکتیویتی گره خورده است، رویداد ON_DESTROY را دریافت میکند. در این رویداد، کامپوننتهای چرخه حیات میتوانند هر چیزی را که لازم دارند قبل از تخریب Activity پاک کنند.
به جای اینکه در Activity خود منطق قرار دهید تا مشخص کنید چرا از بین میرود، از یک شیء ViewModel برای نگهداری دادههای view مربوط به Activity خود استفاده کنید. اگر Activity به دلیل تغییر پیکربندی دوباره ایجاد شود، ViewModel نیازی به انجام کاری ندارد، زیرا ViewModel حفظ شده و به نمونه Activity بعدی داده میشود.
اگر Activity دوباره ایجاد نشود، ViewModel متد onCleared را فراخوانی میکند که در آن میتواند هر دادهای را که نیاز دارد قبل از نابودی پاک کند. میتوانید این دو سناریو را با متد isFinishing تشخیص دهید.
اگر فعالیت در حال اتمام باشد، onDestroy آخرین فراخوانی چرخه عمر است که فعالیت دریافت میکند. اگر onDestroy در نتیجه تغییر پیکربندی فراخوانی شود، سیستم بلافاصله یک نمونه فعالیت جدید ایجاد میکند و سپس onCreate را روی آن نمونه جدید در پیکربندی جدید فراخوانی میکند.
تابع فراخوانی onDestroy تمام منابعی را که توسط توابع فراخوانی قبلی مانند onStop آزاد نشدهاند، آزاد میکند.
ذخیره و بازیابی حالت گذرای رابط کاربری
کاربر انتظار دارد که وضعیت رابط کاربری یک فعالیت در طول تغییر پیکربندی، مانند چرخش یا تغییر به حالت چند پنجرهای، ثابت بماند. با این حال، سیستم به طور پیشفرض هنگام وقوع چنین تغییر پیکربندی، فعالیت را از بین میبرد و هر وضعیت رابط کاربری ذخیره شده در نمونه فعالیت را پاک میکند.
به طور مشابه، یک کاربر انتظار دارد که اگر به طور موقت از برنامه شما به برنامه دیگری برود و بعداً دوباره به برنامه شما برگردد، وضعیت رابط کاربری یکسان باقی بماند. با این حال، سیستم میتواند فرآیند برنامه شما را در حالی که کاربر دور است و فعالیت شما متوقف شده است، از بین ببرد.
وقتی محدودیتهای سیستم، فعالیت را از بین میبرند، وضعیت گذرای رابط کاربری کاربر را با استفاده از ترکیبی از ViewModel ، onSaveInstanceState و/یا ذخیرهسازی محلی حفظ کنید. برای کسب اطلاعات بیشتر در مورد انتظارات کاربر در مقایسه با رفتار سیستم و نحوهی حفظ دادههای پیچیدهی وضعیت رابط کاربری در طول فعالیت آغاز شده توسط سیستم و مرگ فرآیند، به بخش «ذخیره وضعیتهای رابط کاربری» مراجعه کنید.
این بخش به تشریح وضعیت نمونه و نحوه پیادهسازی متد onSaveInstance میپردازد که یک فراخوانی مجدد روی خود اکتیویتی است. اگر دادههای رابط کاربری شما سبک هستند، میتوانید از onSaveInstance به تنهایی برای حفظ وضعیت رابط کاربری در طول تغییرات پیکربندی و مرگ فرآیند آغاز شده توسط سیستم استفاده کنید. اما از آنجا که onSaveInstance هزینههای سریالسازی/غیر سریالسازی را متحمل میشود، در بیشتر موارد، همانطور که در Save UI states ذکر شده است، از ViewModel و onSaveInstance استفاده میکنید.
حالت نمونه
چندین سناریو وجود دارد که در آنها activity شما به دلیل رفتار عادی برنامه از بین میرود، مانند زمانی که کاربر دکمه بازگشت را فشار میدهد یا activity شما با فراخوانی متد finish نابودی خود را اعلام میکند.
وقتی اکتیویتی شما به دلیل فشردن دکمهی برگشت توسط کاربر یا پایان یافتن خود اکتیویتی از بین میرود، هم تصور سیستم و هم تصور کاربر از آن نمونه Activity برای همیشه از بین میرود. در این سناریوها، انتظار کاربر با رفتار سیستم مطابقت دارد و شما هیچ کار اضافی برای انجام دادن ندارید.
با این حال، اگر سیستم به دلیل محدودیتهای سیستم (مانند تغییر پیکربندی یا فشار حافظه) فعالیت را از بین ببرد، اگرچه نمونه واقعی Activity از بین رفته است، سیستم به خاطر میآورد که آن وجود داشته است. اگر کاربر سعی کند به فعالیت برگردد، سیستم با استفاده از مجموعهای از دادههای ذخیره شده که وضعیت فعالیت را هنگام از بین رفتن توصیف میکند، نمونه جدیدی از آن فعالیت ایجاد میکند.
دادههای ذخیرهشدهای که سیستم برای بازیابی حالت قبلی استفاده میکند، حالت نمونه (instance state) نامیده میشود. این مجموعه از جفتهای کلید-مقدار است که در یک شیء Bundle ذخیره میشوند. بهطور پیشفرض، سیستم از حالت نمونه Bundle برای ذخیره اطلاعات مربوط به هر شیء View در طرحبندی فعالیت شما، مانند مقدار متنی که در یک ویجت EditText وارد شده است، استفاده میکند.
بنابراین، اگر نمونه اکتیویتی شما از بین برود و دوباره ایجاد شود، وضعیت طرحبندی بدون نیاز به کدی از سوی شما به حالت قبلی خود بازگردانده میشود. با این حال، اکتیویتی شما ممکن است اطلاعات وضعیت بیشتری داشته باشد که بخواهید آنها را بازیابی کنید، مانند متغیرهای عضو که پیشرفت کاربر را در اکتیویتی پیگیری میکنند.
یک شیء Bundle برای نگهداری بیش از مقدار ناچیزی از دادهها مناسب نیست، زیرا نیاز به سریالسازی در نخ اصلی دارد و حافظه پردازش سیستم را مصرف میکند. برای نگهداری بیش از مقدار بسیار کم دادهها، از یک رویکرد ترکیبی برای نگهداری دادهها استفاده کنید، با استفاده از ذخیرهسازی محلی پایدار، متد onSaveInstanceState و کلاس ViewModel ، همانطور که در Save UI states ذکر شده است.
ذخیره حالت ساده و سبک رابط کاربری با استفاده از onSaveInstanceState
As your activity begins to stop, the system calls the onSaveInstanceState method so your activity can save state information to an instance state bundle. The default implementation of this method saves transient information about the state of the activity's view hierarchy, such as the text in an EditText widget or the scroll position of a ListView widget.
برای ذخیره اطلاعات اضافی در مورد وضعیت نمونه برای فعالیت خود، onSaveInstanceState را بازنویسی کنید و جفتهای کلید-مقدار را به شیء Bundle اضافه کنید که در صورت از بین رفتن غیرمنتظره فعالیت شما ذخیره میشود. وقتی onSaveInstanceState را بازنویسی میکنید، اگر میخواهید پیادهسازی پیشفرض، وضعیت سلسله مراتب نما را ذخیره کند، باید پیادهسازی سوپرکلاس را فراخوانی کنید. این موضوع در مثال زیر نشان داده شده است:
کاتلین
override fun onSaveInstanceState(outState: Bundle?) {
// Save the user's current game state.
outState?.run {
putInt(STATE_SCORE, currentScore)
putInt(STATE_LEVEL, currentLevel)
}
// Always call the superclass so it can save the view hierarchy state.
super.onSaveInstanceState(outState)
}
companion object {
val STATE_SCORE = "playerScore"
val STATE_LEVEL = "playerLevel"
}
جاوا
static final String STATE_SCORE = "playerScore";
static final String STATE_LEVEL = "playerLevel";
// ...
@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
// Save the user's current game state.
savedInstanceState.putInt(STATE_SCORE, currentScore);
savedInstanceState.putInt(STATE_LEVEL, currentLevel);
// Always call the superclass so it can save the view hierarchy state.
super.onSaveInstanceState(savedInstanceState);
}
برای ذخیره دادههای ماندگار، مانند تنظیمات کاربر یا دادههای پایگاه داده، از فرصتهای مناسب زمانی که فعالیت شما در پیشزمینه است استفاده کنید. اگر چنین فرصتی پیش نیامد، دادههای ماندگار را در طول متد onStop ذخیره کنید.
بازیابی وضعیت رابط کاربری فعالیت با استفاده از وضعیت نمونه ذخیره شده
وقتی اکتیویتی شما پس از اینکه قبلاً نابود شده بود، دوباره ایجاد میشود، میتوانید وضعیت نمونه ذخیره شده خود را از Bundle که سیستم به اکتیویتی شما منتقل میکند، بازیابی کنید. هر دو متد فراخوانی onCreate و onRestoreInstanceState Bundle یکسانی را دریافت میکنند که حاوی اطلاعات وضعیت نمونه است.
از آنجایی که متد onCreate چه در زمان ایجاد یک نمونه جدید از اکتیویتی شما و چه در زمان بازسازی یک نمونه قبلی فراخوانی میشود، قبل از تلاش برای خواندن وضعیت Bundle باید بررسی کنید که آیا null است یا خیر. اگر null باشد، سیستم به جای بازیابی نمونه قبلی که از بین رفته است، یک نمونه جدید از اکتیویتی ایجاد میکند.
قطعه کد زیر نشان میدهد که چگونه میتوانید برخی از دادههای وضعیت را در onCreate بازیابی کنید:
کاتلین
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) // Always call the superclass first
// Check whether we're recreating a previously destroyed instance.
if (savedInstanceState != null) {
with(savedInstanceState) {
// Restore value of members from saved state.
currentScore = getInt(STATE_SCORE)
currentLevel = getInt(STATE_LEVEL)
}
} else {
// Probably initialize members with default values for a new instance.
}
// ...
}
جاوا
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // Always call the superclass first
// Check whether we're recreating a previously destroyed instance.
if (savedInstanceState != null) {
// Restore value of members from saved state.
currentScore = savedInstanceState.getInt(STATE_SCORE);
currentLevel = savedInstanceState.getInt(STATE_LEVEL);
} else {
// Probably initialize members with default values for a new instance.
}
// ...
}
به جای بازیابی وضعیت در طول onCreate ، میتوانید onRestoreInstanceState پیادهسازی کنید که سیستم آن را پس از متد onStart فراخوانی میکند. سیستم فقط در صورتی onRestoreInstanceState را فراخوانی میکند که وضعیت ذخیرهشدهای برای بازیابی وجود داشته باشد، بنابراین نیازی به بررسی تهی بودن Bundle ندارید.
کاتلین
override fun onRestoreInstanceState(savedInstanceState: Bundle?) {
// Always call the superclass so it can restore the view hierarchy.
super.onRestoreInstanceState(savedInstanceState)
// Restore state members from saved instance.
savedInstanceState?.run {
currentScore = getInt(STATE_SCORE)
currentLevel = getInt(STATE_LEVEL)
}
}
جاوا
public void onRestoreInstanceState(Bundle savedInstanceState) {
// Always call the superclass so it can restore the view hierarchy.
super.onRestoreInstanceState(savedInstanceState);
// Restore state members from saved instance.
currentScore = savedInstanceState.getInt(STATE_SCORE);
currentLevel = savedInstanceState.getInt(STATE_LEVEL);
}
پیمایش بین فعالیتها
یک برنامه احتمالاً در طول عمر خود، شاید بارها، به یک اکتیویتی وارد و از آن خارج میشود، مانند زمانی که کاربر دکمه بازگشت دستگاه را لمس میکند یا اکتیویتی، اکتیویتی دیگری را اجرا میکند.
این بخش موضوعاتی را پوشش میدهد که برای پیادهسازی موفقیتآمیز انتقال فعالیتها باید بدانید. این مباحث شامل شروع یک فعالیت از فعالیت دیگر، ذخیره وضعیت فعالیت و بازیابی وضعیت فعالیت میشود.
شروع یک فعالیت از فعالیت دیگر
یک فعالیت اغلب نیاز دارد که در مقطعی فعالیت دیگری را آغاز کند. این نیاز زمانی ایجاد میشود که یک برنامه نیاز به انتقال از صفحه فعلی به صفحه جدید داشته باشد.
بسته به اینکه آیا activity شما از activity جدیدی که قرار است شروع شود، نتیجهای میخواهد یا نه، میتوانید activity جدید را با استفاده از متد startActivity یا متد startActivityForResult شروع کنید. در هر صورت، یک شیء Intent به آن ارسال میکنید.
شیء Intent یا دقیقاً فعالیتی را که میخواهید شروع کنید مشخص میکند یا نوع عملی را که میخواهید انجام دهید توصیف میکند. سیستم فعالیت مناسب را برای شما انتخاب میکند، که حتی میتواند از یک برنامه متفاوت باشد. یک شیء Intent همچنین میتواند مقادیر کمی از دادهها را برای استفاده توسط فعالیتی که شروع شده است، حمل کند. برای اطلاعات بیشتر در مورد کلاس Intent ، به Intents و Intent Filters مراجعه کنید.
شروع فعالیت
اگر اکتیویتی تازه شروع شده نیازی به برگرداندن نتیجه نداشته باشد، اکتیویتی فعلی میتواند با فراخوانی متد startActivity آن را شروع کند.
هنگام کار در برنامه خودتان، اغلب نیاز دارید که به سادگی یک activity شناخته شده را راه اندازی کنید. برای مثال، قطعه کد زیر نحوه راه اندازی activity ای به نام SignInActivity را نشان می دهد.
کاتلین
val intent = Intent(this, SignInActivity::class.java)
startActivity(intent)
جاوا
Intent intent = new Intent(this, SignInActivity.class);
startActivity(intent);
همچنین ممکن است برنامه شما بخواهد با استفاده از دادههای activity شما، اقداماتی مانند ارسال ایمیل، پیامک یا بهروزرسانی وضعیت را انجام دهد. در این حالت، ممکن است برنامه شما activity های مخصوص به خود را برای انجام چنین اقداماتی نداشته باشد، بنابراین میتوانید از activity های ارائه شده توسط سایر برنامههای موجود در دستگاه که میتوانند این اقدامات را برای شما انجام دهند، استفاده کنید.
اینجاست که intentها واقعاً ارزشمند میشوند. شما میتوانید یک intent ایجاد کنید که عملی را که میخواهید انجام دهید توصیف میکند و سیستم activity مناسب را از برنامه دیگری اجرا میکند. اگر چندین activity وجود داشته باشد که میتوانند intent را مدیریت کنند، کاربر میتواند انتخاب کند که از کدام یک استفاده کند. به عنوان مثال، اگر میخواهید به کاربر اجازه دهید یک پیام ایمیل ارسال کند، میتوانید intent زیر را ایجاد کنید:
کاتلین
val intent = Intent(Intent.ACTION_SEND).apply {
putExtra(Intent.EXTRA_EMAIL, recipientArray)
}
startActivity(intent)
جاوا
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_EMAIL, recipientArray);
startActivity(intent);
EXTRA_EMAIL اضافی که به intent اضافه شده است، یک آرایه رشتهای از آدرسهای ایمیلی است که قرار است ایمیل به آنها ارسال شود. وقتی یک برنامه ایمیل به این intent پاسخ میدهد، آرایه رشتهای ارائه شده در extra را میخواند و آدرسها را در فیلد "to" فرم ارسال ایمیل قرار میدهد. در این شرایط، activity برنامه ایمیل شروع میشود و وقتی کاربر کار خود را تمام کرد، activity شما از سر گرفته میشود.
شروع فعالیت برای نتیجه
گاهی اوقات میخواهید نتیجهای از یک فعالیت را پس از پایان آن دریافت کنید. برای مثال، ممکن است فعالیتی را شروع کنید که به کاربر اجازه میدهد شخصی را در لیست مخاطبین انتخاب کند. وقتی فعالیت به پایان میرسد، شخصی که انتخاب شده است را برمیگرداند. برای انجام این کار، متد startActivityForResult(Intent, int) را فراخوانی میکنید، که در آن پارامتر عدد صحیح، فراخوانی را مشخص میکند.
این شناسه برای تمایز قائل شدن بین چندین فراخوانی startActivityForResult(Intent, int) از یک اکتیویتی واحد در نظر گرفته شده است. این یک شناسه سراسری نیست و در معرض خطر تداخل با برنامهها یا اکتیویتیهای دیگر قرار ندارد. نتیجه از طریق متد onActivityResult(int, int, Intent) شما بازگردانده میشود.
وقتی یک فعالیت فرزند خارج میشود، میتواند تابع setResult(int) را برای بازگرداندن دادهها به والد خود فراخوانی کند. فعالیت فرزند باید یک کد نتیجه ارائه دهد که میتواند نتایج استاندارد RESULT_CANCELED, RESULT_OK یا هر مقدار سفارشی دیگری باشد که از RESULT_FIRST_USER شروع میشود.
علاوه بر این، اکتیویتی فرزند میتواند به صورت اختیاری یک شیء Intent حاوی هر داده اضافی که میخواهد را برگرداند. اکتیویتی والد از متد onActivityResult(int, int, Intent) به همراه شناسه عدد صحیحی که اکتیویتی والد در ابتدا ارائه داده است، برای دریافت اطلاعات استفاده میکند.
اگر یک فعالیت فرزند به هر دلیلی، مانند از کار افتادن، با شکست مواجه شود، فعالیت والد نتیجهای با کد RESULT_CANCELED دریافت میکند.
کاتلین
class MyActivity : Activity() {
// ...
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
Intent(Intent.ACTION_PICK,Uri.parse("content://contacts")),
PICK_CONTACT_REQUEST)
return true
}
return false
}
override fun onActivityResult(requestCode: Int, resultCode: Int, intent: Intent?) {
when (requestCode) {
PICK_CONTACT_REQUEST ->
if (resultCode == RESULT_OK) {
// A contact was picked. Display it to the user.
startActivity(Intent(Intent.ACTION_VIEW, intent?.data))
}
}
}
companion object {
internal val PICK_CONTACT_REQUEST = 0
}
}
جاوا
public class MyActivity extends Activity {
// ...
static final int PICK_CONTACT_REQUEST = 0;
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
// When the user center presses, let them pick a contact.
startActivityForResult(
new Intent(Intent.ACTION_PICK,
new Uri("content://contacts")),
PICK_CONTACT_REQUEST);
return true;
}
return false;
}
protected void onActivityResult(int requestCode, int resultCode,
Intent data) {
if (requestCode == PICK_CONTACT_REQUEST) {
if (resultCode == RESULT_OK) {
// A contact was picked. Display it to the user.
startActivity(new Intent(Intent.ACTION_VIEW, data));
}
}
}
}
فعالیتهای هماهنگکننده
وقتی یک فعالیت، فعالیت دیگری را آغاز میکند، هر دو دچار گذارهای چرخه حیات میشوند. فعالیت اول متوقف میشود و وارد حالت مکث یا توقف میشود، در حالی که فعالیت دیگر ایجاد میشود. در صورتی که این فعالیتها دادههای ذخیره شده روی دیسک یا جای دیگری را به اشتراک بگذارند، درک این نکته مهم است که فعالیت اول قبل از ایجاد فعالیت دوم به طور کامل متوقف نمیشود. در عوض، فرآیند شروع فعالیت دوم با فرآیند توقف فعالیت اول همپوشانی دارد.
ترتیب فراخوانیهای چرخه عمر به خوبی تعریف شده است، به خصوص زمانی که دو فعالیت در یک فرآیند - به عبارت دیگر، یک برنامه - هستند و یکی دیگری را شروع میکند. در اینجا ترتیب عملیاتی که هنگام شروع فعالیت A توسط فعالیت B رخ میدهد، آمده است:
- متد
onPauseمربوط به فعالیت A اجرا میشود. - متدهای
onCreate،onStartوonResumeمربوط به Activity B به ترتیب اجرا میشوند. Activity B اکنون دارای تمرکز کاربر است. - اگر فعالیت A دیگر روی صفحه نمایش قابل مشاهده نباشد، متد
onStopآن اجرا میشود.
این توالی فراخوانیهای چرخه حیات به شما امکان میدهد انتقال اطلاعات از یک فعالیت به فعالیت دیگر را مدیریت کنید.