الاحتفاظ بالداخل والخارج ViewModel

الصورة

في مرحلة ما ، لاحظت نقاشًا دوريًا حول كيفية عمل ViewModel فعليًا من مكونات Google المعمارية. وإذ أدركت أنني لم أفهم نفسي ، فقد صعدت إلى الإنترنت وفوجئت عندما وجدت أن هناك كمية لا تصدق من المقالات المتطابقة حول كيفية طبخ ViewModel ، وتكوين صداقات مع LiveData ، وتجاوز التبعيات من خلال Dagger ، والجمع مع RxJava ، وعناوين أخرى ذات درجات متفاوتة من الفائدة ، ومع ذلك ، لا يوجد شيء تقريبًا حول ما يجري في الداخل. لذلك سأحاول سد الفجوة بنفسي.

الحذر


TL ؛ DR إذا شعرت بالأسف على الوقت - انتقل إلى الإخراج ، فسوف تفقد القليل.

لذا فإن أول ما يمكنك الانتباه إليه هو وجود مجموعتين مختلفتين من المكونات المعمارية مع ViewModel ، وهما:

1) android.arch.lifecycle القديم
2) androidx.lifecycle جديد

المفسد : لا يوجد فرق كبير بينهما.

كل العمل يكمن وراء التحدي:

ViewModelProviders.of(activity).get(MyViewModel::class.java) 

لنبدأ مع الطريقة

  public static ViewModelProvider of(@NonNull FragmentActivity activity) { return of(activity, null); } public static ViewModelProvider of(@NonNull FragmentActivity activity, @Nullable Factory factory) { Application application = checkApplication(activity); if (factory == null) { factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application); } return new ViewModelProvider(ViewModelStores.of(activity), factory); } 

checkApplication يبحث فقط عن لاغٍ ، و AndroidViewModelFactory هو مجرد مفردة آمنة لمؤشر ترابط تحمل التطبيق. لذلك فهي ليست ذات أهمية خاصة ، والأكثر إثارة للاهتمام هو في طريقة ViewModelStores.of :

  public static ViewModelStore of(@NonNull FragmentActivity activity) { if (activity instanceof ViewModelStoreOwner) { return ((ViewModelStoreOwner) activity).getViewModelStore(); } return holderFragmentFor(activity).getViewModelStore(); } 

للوهلة الأولى ، يبدو غريباً إلى حد ما - لماذا يجب أن يتحقق FragmentActivity من وجود واجهة ViewModelStoreOwner إذا كان يطبقها بالفعل ؟ - لم يكن هذا هو الحال دائمًا - حتى فبراير 2018 البعيد ، عندما تم إصدار إصدار مكتبة الدعم 27.1.0 ، لم يطبق FragmentActivity أبدًا ViewModelStoreOwner. في الوقت نفسه ، عملت ViewModel لنفسها.

لذلك دعونا نبدأ بالحالة القديمة - تم إطلاق طريقة holderFragmentFor :

  public static HolderFragment holderFragmentFor(FragmentActivity activity) { return sHolderFragmentManager.holderFragmentFor(activity); } 

بعد ذلك ، احصل على جزء حامل جديد أو أنشئه:

  HolderFragment holderFragmentFor(FragmentActivity activity) { FragmentManager fm = activity.getSupportFragmentManager(); HolderFragment holder = findHolderFragment(fm); if (holder != null) { return holder; } holder = mNotCommittedActivityHolders.get(activity); if (holder != null) { return holder; } if (!mActivityCallbacksIsAdded) { mActivityCallbacksIsAdded = true; activity.getApplication().registerActivityLifecycleCallbacks(mActivityCallbacks); } holder = createHolderFragment(fm); mNotCommittedActivityHolders.put(activity, holder); return holder; } 

حسنا ، HoldFragment نفسها ، بطبيعة الحال ، الاحتفاظ بها

  public HolderFragment() { setRetainInstance(true); } 

في الواقع ، يتم تخزين كائن ViewModelStore فيه ، والذي بدوره يحمل حزمة من ViewModel :

  public class ViewModelStore { private final HashMap<String, ViewModel> mMap = new HashMap<>(); final void put(String key, ViewModel viewModel) { ViewModel oldViewModel = mMap.put(key, viewModel); if (oldViewModel != null) { oldViewModel.onCleared(); } } final ViewModel get(String key) { return mMap.get(key); } public final void clear() { for (ViewModel vm : mMap.values()) { vm.onCleared(); } mMap.clear(); } } 

دعنا نعود إلى الحالة عندما يكون إصدار مكتبة الدعم 27.1.0 وأعلى. تطبق FragmentActivity بالفعل واجهة ViewModelStoreOwner ، أي أنها تنفذ أسلوب getViewModelStore الوحيد:

  public ViewModelStore getViewModelStore() { if (this.getApplication() == null) { throw new IllegalStateException("Your activity is not yet attached to the Application instance. You can't request ViewModel before onCreate call."); } else { if (this.mViewModelStore == null) { FragmentActivity.NonConfigurationInstances nc = (FragmentActivity.NonConfigurationInstances)this.getLastNonConfigurationInstance(); if (nc != null) { this.mViewModelStore = nc.viewModelStore; } if (this.mViewModelStore == null) { this.mViewModelStore = new ViewModelStore(); } } return this.mViewModelStore; } } 

هنا سأبسط قليلاً - NonConfigurationInaterial هو كائن لا ينبغي أن يعتمد على التكوين (من الواضح من الاسم) ، والذي يكمن في النشاط وعمليات المسح داخل ActivityClientRecord من خلال ActivityThread أثناء الاستجمام بين onStop و onDestroy

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

كان للنشاط دائمًا أسلوب onRetainNonConfigurationInstance المثير للاهتمام. في فئة النشاط ، لم يفعل شيئًا بشكل أساسي. بشكل عام:

  public Object onRetainNonConfigurationInstance() { return null; } 

الوصف في الوثائق واعد:
يطلق عليه النظام ، كجزء من تدمير نشاط بسبب تغيير التكوين ، عندما يكون معروفًا أنه سيتم إنشاء مثيل جديد على الفور للتكوين الجديد. يمكنك إرجاع أي كائن تريده هنا ، بما في ذلك مثيل النشاط نفسه ، والذي يمكن استرجاعه لاحقًا عن طريق استدعاء getLastNonConfigurationInstance () في مثيل النشاط الجديد.

الصورة

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

تم استدعاء طريقة clear () لـ ViewModel ببساطة شديدة - في طريقة onDestroy FragmentActivity.

  protected void onDestroy() { super.onDestroy(); if (this.mViewModelStore != null && !this.isChangingConfigurations()) { this.mViewModelStore.clear(); } this.mFragments.dispatchDestroy(); } 

في الواقع ، مع Androidx كل شيء تقريباً هو نفسه ، والفرق الوحيد هو أن طريقة getViewModelStore () لم تعد في FragmentActivity ، ولكن في ComponentActivity ، التي يتم من خلالها توريث FragmentActivity في AndroidX. تم تغيير استدعاء clear () فقط ؛ لقد تمت إزالته من onDestroy إلى رد اتصال مستقل ، والذي تم إنشاؤه في مُنشئ ComponentActivity:

  getLifecycle().addObserver(new GenericLifecycleObserver() { @Override public void onStateChanged(LifecycleOwner source, Lifecycle.Event event) { if (event == Lifecycle.Event.ON_DESTROY) { if (!isChangingConfigurations()) { getViewModelStore().clear(); } } } }); 

بالنسبة للبروتوكول - أثناء إنشاء المقال ، تم استخدام ما يلي:

مكتبة الدعم 27.0.0 ، 28.0.0
androidx.lifecycle: دورة حياة - viewmodel: 2.0.0
androidx.lifecycle: امتدادات دورة الحياة: 2.0.0
android.arch.lifecycle: الامتدادات: 1.1.1
android.arch.lifecycle: viewmodel: 1.1.1

الاستنتاجات:


- نجح ViewModel حقًا في إعادة تنشيط النشاط في جزء الاحتفاظ به حتى مكتبة الدعم 27.1.0 التي ظهرت في فبراير 2018
- من إصدار مكتبة الدعم 27.1.0 وما بعده ، وكذلك في AndroidX ViewModel ، ذهبت إلى الانتظار لإعادة إنشاء نشاط في FragmentActivity.NonConfigurationInaterial ( ComponentActivity.NonConfigurationInaterial لـ AndroidX) ، في الواقع نفس الآلية التي تعمل من خلالها الاحتفاظ بالشظايا الإضافية ، يتم إرسال جميع ViewModel "بجانب" مع الاحتفاظ شظايا.
- آلية ViewModel هي نفسها تقريباً في AndroidX ومكتبة الدعم
- إذا كنت بحاجة فجأة إلى (نعم لا أستطيع أن أتخيل لماذا) اسحب البيانات التي ينبغي أن تعيش أثناء بقاء النشاط ولكن مع مراعاة الاستجمام - يمكنك استخدام onRetainNonConfigurationInstance () / getLastNonConfigurationInstance ()
- ما القرار القديم ، ما الجديد يبدو وكأنه شيء بين الاختراق موثقة والعكازات

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


All Articles