في مرحلة ما ، لاحظت نقاشًا دوريًا حول كيفية عمل
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 ()- ما القرار القديم ، ما الجديد يبدو وكأنه شيء بين الاختراق موثقة والعكازات