بناءً على المكان الذي يتم فيه رفع الحالة والمنطق المطلوب، يمكنك استخدام واجهات برمجة تطبيقات مختلفة لتخزين حالة واجهة المستخدم واستعادتها. يستخدم كل تطبيق مجموعة من واجهات برمجة التطبيقات لتحقيق ذلك على أفضل وجه.
يمكن أن يفقد أي تطبيق Android حالة واجهة المستخدم بسبب إعادة إنشاء النشاط أو العملية. يمكن أن يحدث هذا الفقدان للحالة بسبب الأحداث التالية:
- تغييرات الإعدادات: يتم تدمير النشاط وإعادة إنشائه ما لم يتم التعامل مع تغيير الإعدادات يدويًا.
- إيقاف العملية نهائيًا من خلال النظام يكون التطبيق في الخلفية ويحرر الجهاز الموارد (مثل الذاكرة) لتستخدمها العمليات الأخرى.
يُعدّ الحفاظ على الحالة بعد هذه الأحداث أمرًا ضروريًا لتوفير تجربة إيجابية للمستخدم. يعتمد اختيار الحالة التي تريد الاحتفاظ بها على مسارات المستخدمين الفريدة في تطبيقك. كأفضل ممارسة، يجب على الأقل الحفاظ على إدخال المستخدم والحالة المتعلقة بالتنقل. وتشمل الأمثلة على ذلك موضع الانتقال في قائمة، أو رقم تعريف العنصر الذي يريد المستخدم معرفة المزيد من التفاصيل عنه، أو الاختيار الجاري للإعدادات المفضّلة للمستخدم، أو الإدخال في حقول النص.
تلخّص هذه الصفحة واجهات برمجة التطبيقات المتاحة لتخزين حالة واجهة المستخدم استنادًا إلى المكان الذي يتم فيه رفع الحالة والمنطق الذي يحتاج إليها.
منطق واجهة المستخدم
إذا تم رفع الحالة في واجهة المستخدم، سواء في الدوال المركّبة أو فئات عنصر الاحتفاظ بالحالة العادية التي يقتصر نطاقها على التكوين، يمكنك استخدام rememberSaveable للاحتفاظ بالحالة في جميع عمليات إعادة إنشاء الأنشطة والعمليات.
في المقتطف التالي، يتم استخدام rememberSaveable لتخزين حالة عنصر واحد في واجهة المستخدم من النوع المنطقي:
@Composable fun ChatBubble( message: Message ) { var showDetails by rememberSaveable { mutableStateOf(false) } ClickableText( text = AnnotatedString(message.content), onClick = { showDetails = !showDetails } ) if (showDetails) { Text(message.timestamp) } }
showDetails هو متغيّر منطقي يخزّن ما إذا كانت فقاعة المحادثة مصغّرة
أو موسّعة.
يخزّن rememberSaveable حالة عنصر في واجهة المستخدم في Bundle من خلال آلية حالة المثيل المحفوظة.
ويمكنه تخزين الأنواع الأساسية في الحزمة تلقائيًا. إذا كانت الحالة
محفوظة في نوع غير أساسي، مثل فئة بيانات، يمكنك استخدام
آليات تخزين مختلفة، مثل استخدام التعليق التوضيحي Parcelize،
أو استخدام واجهات برمجة تطبيقات Compose مثل listSaver وmapSaver، أو تنفيذ
فئة حفظ مخصّصة تعمل على توسيع فئة Saver لوقت تشغيل Compose. لمزيد من المعلومات حول هذه الطرق، يمكنك الاطّلاع على مستندات طرق تخزين الحالة.
في المقتطف التالي، تخزّن واجهة برمجة التطبيقات rememberLazyListState في Compose
LazyListState، الذي يتألف من حالة الانتقال في
LazyColumn أو LazyRow، باستخدام rememberSaveable. ويستخدم
LazyListState.Saver، وهو أداة حفظ مخصّصة يمكنها
تخزين حالة الانتقال واستعادتها. بعد إعادة إنشاء نشاط أو عملية (على سبيل المثال، بعد تغيير في الإعدادات مثل تغيير اتجاه الجهاز)، يتم الحفاظ على حالة الانتقال.
@Composable fun rememberLazyListState( initialFirstVisibleItemIndex: Int = 0, initialFirstVisibleItemScrollOffset: Int = 0 ): LazyListState { return rememberSaveable(saver = LazyListState.Saver) { LazyListState( initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset ) } }
أفضل الممارسات
يستخدم rememberSaveable Bundle لتخزين حالة واجهة المستخدم، والتي تتم مشاركتها من خلال
واجهات برمجة التطبيقات الأخرى التي تكتب إليها أيضًا، مثل استدعاءات onSaveInstanceState() في
نشاطك. ومع ذلك، فإن حجم Bundle هذا محدود، وقد يؤدي تخزين العناصر الكبيرة إلى استثناءات TransactionTooLarge في وقت التشغيل. قد يؤدي ذلك إلى حدوث مشاكل في تطبيقات Activity التي تستخدم صفحة واحدة، حيث يتم استخدام Bundle نفسه في جميع أجزاء التطبيق.
لتجنُّب هذا النوع من الأعطال، يجب عدم تخزين عناصر معقّدة كبيرة أو قوائم عناصر في الحزمة.
بدلاً من ذلك، يمكنك تخزين الحد الأدنى من الحالة المطلوبة، مثل المعرّفات أو المفاتيح، واستخدامها لتفويض استعادة حالة واجهة المستخدم الأكثر تعقيدًا إلى آليات أخرى، مثل مساحة التخزين الدائمة.
تعتمد خيارات التصميم هذه على حالات الاستخدام المحدّدة لتطبيقك وكيفية توقّع المستخدمين لسلوكه.
التحقّق من استعادة الحالة
يمكنك التحقّق من أنّ الحالة المخزَّنة باستخدام rememberSaveable في عناصر Compose تتم استعادتها بشكل صحيح عند إعادة إنشاء النشاط أو العملية. تتوفّر واجهات برمجة تطبيقات محدّدة لتحقيق ذلك، مثل
StateRestorationTester. يمكنك الاطّلاع على مستندات الاختبار لمزيد من المعلومات.
منطق النشاط التجاري
إذا تم رفع حالة عنصر واجهة المستخدم إلى ViewModel لأنّها مطلوبة من خلال منطق النشاط التجاري، يمكنك استخدام واجهات برمجة التطبيقات في ViewModel.
من المزايا الرئيسية لاستخدام ViewModel في تطبيق Android
أنّه يتعامل مع تغييرات الإعدادات مجانًا. عند حدوث تغيير في الإعدادات، يتم تدمير النشاط وإعادة إنشائه، ويتم الاحتفاظ بحالة واجهة المستخدم التي تم نقل قيمتها إلى ViewModel في الذاكرة. بعد إعادة الإنشاء، يتم إرفاق مثيل ViewModel القديم
بمثيل النشاط الجديد.
ومع ذلك، لا يستمر مثيل ViewModel في حالة إيقاف العملية نهائيًا من قِبل النظام.
للحفاظ على حالة واجهة المستخدم، استخدِم وحدة Saved State لـ
ViewModel التي تحتوي على واجهة برمجة التطبيقات SavedStateHandle.
أفضل الممارسات
يستخدم SavedStateHandle أيضًا آلية Bundle لتخزين حالة واجهة المستخدم، لذا يجب استخدامه فقط لتخزين حالة عنصر في واجهة المستخدم البسيطة.
حالة واجهة مستخدم الشاشة، التي يتم إنتاجها من خلال تطبيق قواعد النشاط التجاري والوصول إلى
طبقات التطبيق بخلاف واجهة المستخدم، يجب عدم تخزينها في
SavedStateHandle بسبب تعقيدها وحجمها المحتمل. يمكنك استخدام
آليات مختلفة لتخزين البيانات المعقّدة أو الكبيرة، مثل مساحة التخزين
المستمرّة المحلية. بعد إعادة إنشاء العملية، تتم إعادة إنشاء الشاشة مع
الحالة المؤقتة التي تم تخزينها في SavedStateHandle (إن وُجدت)، ويتم
إنتاج حالة واجهة مستخدم الشاشة مرة أخرى من طبقة البيانات.
واجهات برمجة تطبيقات SavedStateHandle
تتضمّن SavedStateHandle واجهات برمجة تطبيقات مختلفة لتخزين حالة عناصر واجهة المستخدم، وأبرزها:
إنشاء State |
saveable() |
|---|---|
StateFlow |
getStateFlow() |
إنشاء State
استخدِم واجهة برمجة التطبيقات saveable في SavedStateHandle لقراءة حالة عنصر واجهة المستخدم وكتابتها على أنّها MutableState، لكي تظلّ الحالة محفوظة عند إعادة إنشاء النشاط والعملية بأقل قدر من الرموز البرمجية.
تتيح واجهة برمجة التطبيقات saveable استخدام الأنواع الأساسية تلقائيًا وتتلقّى مَعلمة stateSaver لاستخدام أدوات الحفظ المخصّصة، تمامًا مثل rememberSaveable().
في المقتطف التالي، يخزّن message إدخال المستخدم الذي تم كتابته في
TextField:
class ConversationViewModel( savedStateHandle: SavedStateHandle ) : ViewModel() { var message by savedStateHandle.saveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) } private set fun update(newMessage: TextFieldValue) { message = newMessage } /*...*/ } val viewModel = ConversationViewModel(SavedStateHandle()) @Composable fun UserInput(/*...*/) { TextField( value = viewModel.message, onValueChange = { viewModel.update(it) } ) }
يمكنك الاطّلاع على مستندات SavedStateHandle لمزيد من المعلومات حول استخدام واجهة برمجة التطبيقات saveable.
StateFlow
استخدِم getStateFlow() لتخزين حالة عنصر في واجهة المستخدم واستخدامه كتدفق من SavedStateHandle. إنّ StateFlow للقراءة فقط،
وتتطلّب واجهة برمجة التطبيقات تحديد مفتاح لكي تتمكّن من استبدال التدفق
لإصدار قيمة جديدة. باستخدام المفتاح الذي تم ضبطه، يمكنك استرداد StateFlow
وجمع أحدث قيمة.
في المقتطف التالي، savedFilterType هو متغيّر StateFlow يخزّن نوع فلتر مطبَّق على قائمة قنوات محادثة في تطبيق محادثات:
private const val CHANNEL_FILTER_SAVED_STATE_KEY = "ChannelFilterKey" class ChannelViewModel( channelsRepository: ChannelsRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val savedFilterType: StateFlow<ChannelsFilterType> = savedStateHandle.getStateFlow( key = CHANNEL_FILTER_SAVED_STATE_KEY, initialValue = ChannelsFilterType.ALL_CHANNELS ) private val filteredChannels: Flow<List<Channel>> = combine(channelsRepository.getAll(), savedFilterType) { channels, type -> filter(channels, type) }.onStart { emit(emptyList()) } fun setFiltering(requestType: ChannelsFilterType) { savedStateHandle[CHANNEL_FILTER_SAVED_STATE_KEY] = requestType } /*...*/ } enum class ChannelsFilterType { ALL_CHANNELS, RECENT_CHANNELS, ARCHIVED_CHANNELS }
في كل مرة يختار فيها المستخدم نوع فلتر جديدًا، يتم استدعاء setFiltering. يؤدي هذا إلى حفظ قيمة جديدة في SavedStateHandle مخزّنة باستخدام المفتاح _CHANNEL_FILTER_SAVED_STATE_KEY_. savedFilterType هو تدفق يعرض
آخر قيمة تم تخزينها في المفتاح. يتم اشتراك filteredChannels في التدفق لإجراء تصفية القناة.
يمكنك الاطّلاع على مستندات SavedStateHandle لمزيد من المعلومات حول واجهة برمجة التطبيقات getStateFlow().
ملخّص
يلخّص الجدول التالي واجهات برمجة التطبيقات التي يتناولها هذا القسم، ومتى يجب استخدام كل منها لحفظ حالة واجهة المستخدم:
| الحدث | منطق واجهة المستخدم | المنطق التجاري في ViewModel |
|---|---|---|
| تغييرات في الإعدادات | rememberSaveable |
تلقائي |
| إيقاف العملية نهائيًا من خلال النظام | rememberSaveable |
SavedStateHandle |
تعتمد واجهة برمجة التطبيقات التي يجب استخدامها على مكان الاحتفاظ بالحالة والمنطق الذي تتطلبه. بالنسبة إلى الحالة المستخدَمة في منطق واجهة المستخدم، استخدِم rememberSaveable. بالنسبة إلى
الحالة المستخدَمة في منطق النشاط التجاري، إذا كنت تحتفظ بها في ViewModel،
يمكنك حفظها باستخدام SavedStateHandle.
يجب استخدام واجهات برمجة التطبيقات للحِزم (rememberSaveable وSavedStateHandle)
لتخزين كميات صغيرة من حالة واجهة المستخدم. هذه البيانات هي الحد الأدنى اللازم لاستعادة
واجهة المستخدم إلى حالتها السابقة، إلى جانب آليات التخزين الأخرى. على سبيل المثال، إذا كنت تخزّن معرّف ملف شخصي كان المستخدم يبحث عنه في الحزمة، يمكنك جلب بيانات ثقيلة، مثل تفاصيل الملف الشخصي، من طبقة البيانات.
لمزيد من المعلومات حول الطرق المختلفة لحفظ حالة واجهة المستخدم، يُرجى الاطّلاع على مستندات حفظ حالة واجهة المستخدم العامة وصفحة طبقة البيانات في دليل البنية.
التطبيقات المقترَحة لك
- ملاحظة: يتم عرض نص الرابط عندما يكون JavaScript غير مفعَّل
- أماكن رفع الحالة
- State وJetpack Compose
- القوائم والشبكات