حفظ الدول في تطبيقات Android

اليوم أردت أن أشارك معك طريقة أخرى للحفاظ على الحالة عند تطوير تطبيقات Android. ليس سراً أن تطبيقنا في الخلفية يمكن أن يقتل في أي وقت وتصبح هذه المشكلة أكثر إلحاحًا مع إدخال توفير الطاقة العدواني - مرحبًا Oreo . أيضا ، لم يقم أحد بإلغاء تغيير التكوين على الهاتف: الاتجاه ، تغيير اللغة ، إلخ. ومن أجل فتح التطبيق من الخلفية وعرض الواجهة في الحالة الأخيرة ، نحتاج إلى الاهتمام بحفظه. أوه ، هذا onSaveInstanceState .

onSaveInstanceState

كم من الألم أحضرنا.

سأعطي أمثلة لاحقًا باستخدام Clean Ach architecture و Dagger2 ، لذا كن مستعدًا لهذا :)

يمكن حل مشكلة الحفاظ على الحالة اعتمادًا على المهام بعدة طرق:

  1. احفظ البيانات الأساسية في onSaveInstanceState للمضيف (نشاط ، جزء) - مثل معرف الصفحة والمستخدم وما إلى ذلك. ما نحتاجه للحصول على البيانات الأولية وعرض الصفحة.
  2. حفظ البيانات المستلمة في البرنامج التفاعلي في المستودع (SharedPreference ، قاعدة البيانات.
  3. استخدم أجزاء الريثين لحفظ البيانات واستعادتها عند إعادة النشاط.

ولكن ماذا لو كنا بحاجة إلى استعادة حالة واجهة المستخدم ، بالإضافة إلى رد الفعل الحالي للواجهة على إجراء المستخدم؟ من أجل البساطة ، دعونا نفكر في حل هذه المشكلة باستخدام مثال حقيقي. لدينا صفحة تسجيل دخول - يقوم المستخدم بإدخال بياناته ، والنقر على الزر ، ثم يتلقى مكالمة واردة. يذهب طلبنا إلى الخلفية. النظام يقتله. يبدو مخيفا ، أليس كذلك؟)

يعود المستخدم إلى التطبيق وماذا يراه؟ كحد أدنى ، مواصلة عملية تسجيل الدخول وإظهار التقدم. إذا تمكن التطبيق من تسجيل الدخول قبل استدعاء طريقة onDestroy الخاصة بالمضيف ، فسيرى المستخدم التنقل إلى شاشة بدء التطبيق. يمكن حل هذا السلوك بسهولة باستخدام جهاز الحالة. تقرير جيد جدا من ياندكس . في نفس المقالة سأحاول مشاركة أفكاري التي تمضغها على هذا التقرير.

الآن كود صغير:

Basestate

public interface BaseState<VIEW extends BaseView, OWNER extends BaseOwner> extends Parcelable{ /** * Get name * * @return name */ @NonNull String getName(); /** * Enter to state * * @param aView view */ void onEnter(@NonNull VIEW aView); /** * Exit from state */ void onExit(); /** * Return to next state */ void forward(); /** * Return to previous state */ void back(); /** * Invalidate view * * @param aView view */ void invalidateView(@NonNull VIEW aView); /** * Get owner * * @return owner */ @NonNull OWNER getOwner(); /** * Set owner * * @param aOwner owner */ void setOwner(@NonNull OWNER aOwner); } 

ضابط القاعدة

 public interface BaseOwner<VIEW extends BaseView, STATE extends BaseState> extends BasePresenter<VIEW>{ /** * Set state * * @param aState state */ void setState(@NonNull STATE aState); } 

BasestateImpl

 public abstract class BaseStateImpl<VIEW extends BaseView, OWNER extends BaseOwner> implements BaseState<VIEW, OWNER>{ private OWNER mOwner; @NonNull @Override public String getName(){ return getClass().getName(); } @Override public void onEnter(@NonNull final VIEW aView){ Timber.d( getName()+" onEnter"); //depend from realization } @Override public void onExit(){ Timber.d(getName()+" onExit"); //depend from realization } @Override public void forward(){ Timber.d(getName()+" forward"); onExit(); //depend from realization } @Override public void back(){ Timber.d(getName()+" back"); onExit(); //depend from realization } @Override public void invalidateView(@NonNull final VIEW aView){ Timber.d(getName()+" invalidateView"); //depend from realization } @NonNull @Override public OWNER getOwner(){ return mOwner; } @Override public void setOwner(@NonNull final OWNER aOwner){ mOwner = aOwner; } 

في حالتنا ، سيكون صاحب الدولة مقدم.

بالنظر إلى صفحة تسجيل الدخول ، يمكن تمييز ثلاث حالات فريدة:

LoginInitState ، LoginProgressingState ، LoginCompleteState .

لذا ، ضع في اعتبارك الآن ما يحدث في هذه الدول.

LoginInitState لدينا التحقق من صحة الحقول وفي حالة نجاح التحقق ، يصبح زر تسجيل الدخول نشطًا.

يتم إجراء طلب تسجيل الدخول في LoginProgressingState ، ويتم حفظ رمز مميز ، ويتم إجراء طلبات إضافية لبدء النشاط الرئيسي للتطبيق.

ينتقل LoginCompleteState إلى الشاشة الرئيسية للتطبيق.

بشكل مشروط ، يمكن عرض الانتقال بين الحالات في الرسم البياني التالي:

رسم تخطيطي لحالة تسجيل الدخول

يتم إنهاء حالة LoginProgressingState إذا نجحت عملية تسجيل الدخول في حالة LoginCompleteState ، وإذا فشلت LoginInitState . وهكذا ، عندما تنفصل وجهة نظرنا ، تكون لدينا حالة حتمية تمامًا للمقدم. يجب علينا حفظ هذه الحالة باستخدام آلية android القياسية onSaveInstanceState . من أجل القيام بذلك ، يجب على جميع حالات تسجيل الدخول تنفيذ واجهة Parcelable . لذلك ، نقوم بتوسيع الواجهة الأساسية BaseState .

بعد ذلك ، لدينا سؤال ، كيف نحيل هذه الدولة من المقدم إلى مضيفنا؟ أسهل طريقة هي أن تطلب من المقدم بيانات من المضيف ، ولكن من وجهة نظر الهندسة المعمارية ، فإن هذا لا يبدو جيدًا جدًا. وهكذا تأتي الشظايا لمساعدتنا. يمكننا إنشاء واجهة لذاكرة التخزين المؤقت وتنفيذها في هذا الجزء:

 public interface Cache{ /** * Save cache data * * @param aData data */ void saveCacheData(@Nullable Parcelable aData); @Nullable Parcelable getCacheData(); /** * Check that cache exist * * @return true if cache exist */ boolean isCacheExist(); } 

بعد ذلك ، نقوم بحقن جزء ذاكرة التخزين المؤقت في مُنشئ المتفاعل ، مثل ذاكرة التخزين المؤقت. نضيف طرقًا في أداة التكامل للحصول على الحالة وحفظها في ذاكرة التخزين المؤقت. الآن ، مع كل تغيير في حالة مقدم العرض ، يمكننا حفظ الحالة في المتفاعل ، ويحفظ المتفاعل بدوره في ذاكرة التخزين المؤقت. يصبح كل شيء منطقيًا جدًا. عندما يتم تحميل المضيف لأول مرة ، يتلقى مقدم العرض الحالة من المتفاعل ، والذي بدوره يتلقى البيانات من ذاكرة التخزين المؤقت. هذه هي الطريقة التي تبدو بها طريقة تغيير الحالة في مقدم العرض:

 @Override public void setState(@NonNull final LoginBaseState aState){ mState.onExit(); mState = aState; clearDisposables(); mState.setOwner(this); mState.onEnter(getView()); mInteractor.setState(mState); } 

أود أن أشير إلى هذه النقطة - يمكن إجراء حفظ البيانات من خلال ذاكرة التخزين المؤقت لأي بيانات ، وليس فقط للدولة. قد تحتاج إلى إنشاء ذاكرة التخزين المؤقت الفريدة للمقتطفات الخاصة بك لتخزين البيانات الحالية. توضح هذه المقالة نهجًا عامًا. وأود أيضا أن أشير إلى أن الحالة المعنية مبالغ فيها للغاية. في الحياة لديك لحل المشاكل أكثر تعقيدا. على سبيل المثال ، تم دمج ثلاث صفحات في طلبنا: تسجيل الدخول ، التسجيل ، استعادة كلمة المرور. في هذه الحالة ، كان مخطط الحالة كما يلي:

مخطط الدولة في مشروع حقيقي

ونتيجة لذلك ، باستخدام نمط الحالة والمنهج الموصوفين في المقالة ، تمكنا من جعل الشفرة أكثر قابلية للقراءة وصيانتها. والمهم هو استعادة الوضع الحالي للتطبيق.

يمكن الاطلاع على الرمز الكامل في المستودع .

Source: https://habr.com/ru/post/ar420023/


All Articles