2824145-c81f384e661dd388.JPG
成都.JPG

前言

ViewModel 作为 Jetpack 中的明星组件,相信大家都对其有一定的了解。在 Google 的官方介绍中也详细的罗列了 ViewModel 的优点,如:

  1. 可以提供和管理UI界面数据。(将加载数据与数据恢复从 Activity or Fragment中解耦)
  2. 可感知生命周期的组件。
  3. 不会因配置改变而销毁。
  4. 可以配合 LiveData 使用。
  5. 多个 Fragment 可以共享同一 ViewModel。
  6. 等等等....

你也可以通过下列两个视频,更为详细的了解 ViewModel:

  • ViewMode男生讲解版
  • ViewMode女生讲解版

在本篇文章中,不会讲解 ViewModel 的使用方式及使用 ViewModel 的原因,而是着重于讲解 ViewModel 的原理。通过阅读本篇文章你能了解到:

  • ViewModel 在 Activity 中的绑定过程。
  • ViewModel 在 Activity 中不会因配置改变而销毁的原理。
  • ViewModel 在 Fragment 中的绑定过程。
  • ViewModel 在 Fragment 中不会因配置改变而销毁的原理。
  • ViewMode 能在 Fragment 中共享的原理。

希望通过该篇文章,大家能对 ViewModel 有更深入的了解。

ViewModel 与 Activity 的绑定过程

一般情况下使用 ViewModel,我们一般会先声明自己的 ViewModel,并在 Activity 中的 onCreate 方法中使用 ViewModelProviders 来创建 ViewModel。 如下代码所示:

 MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);

在谷歌的最新代码中,不推荐使用 ViweModelProviders(注意是有s的呦) ,而是直接使用 ViewModelProvider 的构造函数来创建 ViewModelProvider 对象。

通过使用 ViewModelProviders 类的 of() 方法,我们会得到一个 ViewModelProvider 对象。如下代码所示:

   public static ViewModelProvider of(@NonNull FragmentActivity activity) {return new ViewModelProvider(activity);}

ViewModelProvider 类需要我们传递 ViewModelStoreFactory 对象。其构造函数声明如下:

    //使用ViewModelStoreOwner对象构造函数public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory(): NewInstanceFactory.getInstance());}//使用ViewModelStoreOwner与Factory对象的构造函数public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {this(owner.getViewModelStore(), factory);}//使用ViewModelStore与Factory对象的构造函数public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {mFactory = factory;mViewModelStore = store;}

在 ViewModelProvider 内部,拥有三种类型构造函数:

  • (ViewModelStoreOwner owner):
    • 该构造函数使用 owner 对象的 getViewModelStore() 方法来获取 ViewModelStore 对象,如果传入的 owner 对象也实现了 HasDefaultViewModelProviderFactory 接口时,那么会调用 getDefaultViewModelProviderFactory() 方法获取 Factory。反之,使用内部静态的 NewInstanceFactory 对象来创建 Factory 对象。
  • (ViewModelStoreOwner owner, Factory factory):
    • 该构造函数使用 owner 对象的 getViewModelStore() 方法来获取 ViewModelStore 对象,使用传递的 Factory 对象
  • (ViewModelStore store, Factory factory)
    • 使用 ViewModelStoreFactory 对象的构造函数

Factory 接口介绍

在 ViewModelProvider 中,Factory 主要用于创建 ViewModel,Factory 的声明如下:

    public interface Factory {/*** 通过给定的Class对象创建ViewModel对象* <p>** @param modelClass 所需ViewModel的Class对象* @param <T>        ViewModel的泛型参数* @return 新创建的ViewModel对象*/@NonNull<T extends ViewModel> T create(@NonNull Class<T> modelClass);}

通过实现 Factory 接口,我们可以实现自己想要的工厂以创建所需的 ViewModel。在 Android 中有多个类都实现了该接口(如 KeyedFactory, AndroidViewModelFactory),这里以默认的 NewInstanceFactory 为例:

    public static class NewInstanceFactory implements Factory {private static NewInstanceFactory sInstance;@NonNullstatic NewInstanceFactory getInstance() {if (sInstance == null) {sInstance = new NewInstanceFactory();}return sInstance;}@SuppressWarnings("ClassNewInstance")@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {try {//默认使用对应ViewModel类无参的构造函数创建实例对象return modelClass.newInstance();} catch (InstantiationException e) {throw new RuntimeException("Cannot create an instance of " + modelClass, e);} catch (IllegalAccessException e) {throw new RuntimeException("Cannot create an instance of " + modelClass, e);}}}

默认情况下, NewInstanceFactory 会调用 ViewModel 的无参构造函数创建实例对象,当然如果你需要在 ViewModel 中使用其他参数,你也可以传递自定义的 Factory。

ViewModelStore 介绍

ViewModelStore 内部维护了一个 HashMap,其 key 为 DEFAULT_KEY + ViewModel的Class对象底层类规范名称,其 value 为对应 ViewModel 对象。每个 Activity 与 Fragment 都对应着一个 ViewModelStore ,用于存储所需的 ViewModel。ViewModelStore 类声明如下所示:

DEFAULT_KEY 值为:"androidx.lifecycle.ViewModelProvider.DefaultKey"

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);}Set<String> keys() {return new HashSet<>(mMap.keySet());}/*** 当内部的 ViewModel 不再使用时,清除所占的内存*/public final void clear() {for (ViewModel vm : mMap.values()) {//下面调用ViewModel的clear方法vm.clear();}mMap.clear();}
}

Activity 中创建与获取 ViewModel 流程

ViewModel 最终的创建与获取,需要 ViewProvider 类调用 get(Class<T> modelClass)方法(该方法内部通过 ViewModelStore 与 Factory 的配合,创建并保存了所需的 ViewModel 对象),具体代码如下所示:

 public <T extends ViewModel> T get(@NonNull Class<T> modelClass) {String canonicalName = modelClass.getCanonicalName();if (canonicalName == null) {throw new IllegalArgumentException("Local and anonymous classes can not be ViewModels");}return get(DEFAULT_KEY + ":" + canonicalName, modelClass);}

该方法内部会调用 get(String key, Class<T> modelClass) 方法:

 public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {//👇根据key值从ViewModelStore中取对应的ViewModelViewModel viewModel = mViewModelStore.get(key);//👇判断所传入的Class对象是否是ViewModel的Class类或其子类的对象,如果是,直接返回if (modelClass.isInstance(viewModel)) {if (mFactory instanceof OnRequeryFactory) {((OnRequeryFactory) mFactory).onRequery(viewModel);}return (T) viewModel;} else {//noinspection StatementWithEmptyBodyif (viewModel != null) {// TODO: log a warning.}}//👇如果为null,根据传入的Factory创建新的VideModelif (mFactory instanceof KeyedFactory) {viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);} else {viewModel = (mFactory).create(modelClass);}//👇将新的 ViewModel 存入ViewModelStore,并返回mViewModelStore.put(key, viewModel);return (T) viewModel;}

在该方法中,会在 ViewModelStore 中根据传入的 key 获取并保存 ViewModel。其具体逻辑如下:

  • 根据 key 值从 ViewModelStore 中取对应的 ViewModel。
  • 判断所传入的 Class 对象是否是 ViewModel 的 Class 类或其子类的对象,如果是,直接返回。(当 Object.isInstance(class) 接受的参数为 null 时,该方法会返回 false
  • 如果获取的 ViewModel 为 null,会根据传入的 Factory 对象创建新的 VideModel,并将创建好的 ViewModel 放入 ViewModelStore中。

Activity 中创建与获取 ViewModel 的整体流程如下所示:

2824145-fecc9582d2892c82.png
Activity下ViewModel的创建过程.png

ViewModel 在 Activity 中不会因配置改变而销毁的原理

我们都知道 ViewModel 不会因为 Activity 的配置发生改变而销毁,ViewModel 作用域如下所示:

2824145-10c47be88330a248.png
viewmodel-lifecycle.png

观察上图,我相信小伙伴们肯定有如下疑惑:

  • 当 Activity 因配置发生改变时,系统会重新创建一个新的 Activity 。那老的 Activity 中的 ViewModel 是如何传递给新的 Activity 的呢?
  • ViewModel 又是如何感知配置是否改变,进而判断是否销毁的呢?

要解决如上问题,我们需要了解 Android 中数据恢复的方式以及 Activity 生命周期中 ViewModel 实际处理流程。

数据恢复的常见方式

在 Android 系统中,需要数据恢复有如下两种场景:

  • 场景1:资源相关的配置发生改变导致 Activity 被杀死并重新创建。
  • 场景2:资源内存不足导致低优先级的 Activity 被杀死。

针对上述场景,分别对应三种不同的数据恢复方式。

对应场景1,不考虑在清单文件中配置 android:configChanges 的特殊情况。

使用 onSaveInstanceState 与 onRestoreInstanceState

使用 onSaveInstanceState 与 onRestoreInstanceState 方法,能处理场景1与场景2的情况。当你的界面数据简单且轻量时,例如原始数据类型或简单对象(比如 String),则我们可以采用该方式。如果你需要恢复的数据较为复杂,那你应该考虑使用 ViewModle + onSaveInstanceState() (为什么要配合使用,会在下文进行讲解),因为使用 onSaveInstanceState() 会导致序列化或反序列化,而这,有一定的时间消耗。

onSaveInstanceState() 更为详细的介绍以及使用,可参考官方文档:

  • 使用 onSaveInstanceState() 保存简单轻量的界面状态
  • 使用保存的实例状态恢复 Activity 界面状态

使用 Fragment 的 setRetainInstance

当配置发生改变时,Fragment 会随着宿主 Activity 销毁与重建,当我们调用 Fragment 中的 setRetainInstance(true) 方法时,系统允许 Fragment 绕开销毁-重建的过程。使用该方法,将会发送信号给系统,让 Activity 重建时,保留 Fragment 的实例。需要注意的是:

  • 使用该方法后,不会调用 Fragment 的 onDestory() 方法,但仍然会调用 onDetach() 方法
  • 使用该方法后,不会调用 Fragment 的 onCreate(Bundle) 方法。因为 Fragment 没有被重建。
  • 使用该方法后,Fragment 的 onAttach(Activity)onActivityCreated(Bundle) 方法仍然会被调用。

以下示例代码展示了如何在配置发生改变时,保留 Fragment 实例,并进行数据的恢复。

public class MainActivity extends AppCompatActivity {private SaveFragment mSaveFragment;public static final String TAG_SAVE_FRAGMENT = "save_fragment";@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);FragmentManager fm = getSupportFragmentManager();mSaveFragment = (SaveFragment) fm.findFragmentByTag(TAG_SAVE_FRAGMENT);// fragment 不为空,是因为配置发生改变,Fragment 被重建if (mSaveFragment == null) {mSaveFragment = SaveFragment.newInstance();fm.beginTransaction().add(mSaveFragment, TAG_SAVE_FRAGMENT).commit();}//获取保存的数据int saveData = mSaveFragment.getSaveData();}
}

Fragment :

public class SaveFragment extends Fragment {private int saveData;public static SaveFragment newInstance() {Bundle args = new Bundle();SaveFragment fragment = new SaveFragment();fragment.setArguments(args);return fragment;}@Overridepublic void onAttach(@NonNull Context context) {super.onAttach(context);}@Overridepublic void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);//保存当前Fragment实例setRetainInstance(true);saveData = 1010;//通过网络请求或查询数据库,赋值需要保存的数据}@Overridepublic void onDetach() {super.onDetach();}public int getSaveData() {return saveData;}
}

关于 Fragment 的 setRetainInstance 更多用法与注意事项,可以参看文章
Handling Configuration Changes with Fragments

使用 onRetainNonConfigurationInstance 与 getLastNonConfigurationInstance

在 Activity 中提供了 onRetainNonConfigurationInstance 方法,用于处理配置发生改变时数据的保存。随后在重新创建的 Activity 中调用 getLastNonConfigurationInstance 获取上次保存的数据。我们不能直接重写上述方法,如果想在 Activity 中自定义想要恢复的数据,需要我们调用上述两个方法的内部方法:

  • onRetainCustomNonConfigurationInstance()
  • getLastCustomNonConfigurationInstance()

注意:onRetainNonConfigurationInstance 方法系统调用时机介于 onStop - onDestory 之间,getLastNonConfigurationInstance 方法可在 onCreateonStart 方法中调用。

以下代码展示了,在 Actiity 中恢复自定义的数据:

public class MainActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);String name = (String) getLastCustomNonConfigurationInstance();if (!TextUtils.isEmpty(name)) {//获取恢复后的数据,执行相应操作}}//你可以可以在onStart中,获取恢复的数据
//    @Override
//    protected void onStart() {
//        super.onStart();
//        String name = (String) getLastCustomNonConfigurationInstance();
//        if (!TextUtils.isEmpty(name)) {
//        }
//    }@Nullable@Overridepublic Object onRetainCustomNonConfigurationInstance() {return "AndyJennifer";}  
}

在 Android 3.0 后,官方推荐使用 Fragment#setRetainInstance(true) 的方式进行数据的恢复。之所以推荐这种方式,个人猜测是为了降低 Activity 的冗余,将数据恢复的任务从 Activity 抽离出来,这更符合单一职责的设计模式。

几种数据恢复方式的总结

通过了解数据恢复的几种方式,我们能得到如下对比图:

2824145-964abf91880376c1.png
数据恢复对比.png

ViewModel 的恢复

ViewModel 在官方设计之初就倾向于在配置改变时进行数据的恢复。考虑到数据恢复时的效率,官方最终采用了 onRetainNonConfigurationInstance 的方式来恢复 ViewModel 。

在 SDK 27 之前,官方一直采用 Fragment#setRetainInstance(true) 的方式恢复数据。导致官方修改了其内部实现的原因,猜测是因为 Fragment 的坑,程序的扩展性等其他因素。

知道了 ViewModel 的恢复方式,那现在一起来解决我们之前的疑惑。当 Activity 因配置发生改变时,系统会重新创建一个新的 Activity 。那老的 Activity 中的 ViewModel 是如何传递给新的 Activity ?

在 Androidx 中的 Activity 的最新代码中,官方重写了 onRetainNonConfigurationInstance 方法,在该方法中保存了 ViewModelStore (ViweModelStore 中存储了 ViewModel ),进而也保存了 ViewModel,具体代码如下所示:

    public final Object onRetainNonConfigurationInstance() {Object custom = onRetainCustomNonConfigurationInstance();ViewModelStore viewModelStore = mViewModelStore;if (viewModelStore == null) {NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance();if (nc != null) {viewModelStore = nc.viewModelStore;}}if (viewModelStore == null && custom == null) {return null;}//将ViewModel存储在 NonConfigurationInstances 对象中NonConfigurationInstances nci = new NonConfigurationInstances();nci.custom = custom;nci.viewModelStore = viewModelStore;return nci;}

当新的 Activity 重新创建,并调用 ViewModelProviders.of(this).get(xxxModel.class) 时,又会在 getViewModelStore() 方法中获取老 Activity 保存的 ViewModelStore。那么也就拿到了 ViewModel。具体代码如下所示:

    public ViewModelStore getViewModelStore() {if (getApplication() == null) {throw new IllegalStateException("Your activity is not yet attached to the "+ "Application instance. You can't request ViewModel before onCreate call.");}if (mViewModelStore == null) {//👇获取保存的NonConfigurationInstances,NonConfigurationInstances nc =(NonConfigurationInstances) getLastNonConfigurationInstance();if (nc != null) {//👇从该对象中获取ViewModelStoremViewModelStore = nc.viewModelStore;}if (mViewModelStore == null) {mViewModelStore = new ViewModelStore();}}return mViewModelStore;}

ViewModel 何时判断是否被移除

ViewModel 最重要的特性就是不会在配置发生改变的时候被移除。其内部实现也非常简单,监听 Activity 声明周期,在 onDestory 方法被调用时,判断配置是否改变。如果没有发送改变,则调用 Activity 中的 ViewModelStore 的 clear() 方法,清除所有的 ViewModel。具体代码如下所示:

    public ComponentActivity() {Lifecycle lifecycle = getLifecycle();//省略更多....getLifecycle().addObserver(new LifecycleEventObserver() {@Overridepublic void onStateChanged(@NonNull LifecycleOwner source,@NonNull Lifecycle.Event event) {if (event == Lifecycle.Event.ON_DESTROY) {if (!isChangingConfigurations()) {//👇在配置没发生改变且走到onDestory方法时,清除所有的ViewModelgetViewModelStore().clear();}}}});}

ViewModel 在 Fragment 的绑定过程

在官方的最新代码实现中,Fragment 中的 ViewModel 与其宿主 Activity 有着密切的联系。要了解 ViewModel 与 Fragment 的绑定过程,我们需要先了解 FragmentManagerFragmentManagerViewModel 相关知识。

FragmentManager 介绍

每个 Fragment 及宿主 Activity (继承自 FragmentActivity)都会在创建时,初始化一个 FragmentManager 对象,了解 Fragment 中的 ViewModel 与 Activity 的联系的关键,就是理清这些不同阶级的栈视图。

下面给出一个简要的关系图:

2824145-9d85d056fb02e43c.png
FragmentManager栈对应关系.png
  • 对于宿主 Activity , getSupportFragmentManager()获取的是 FragmentActivity 的 FragmentManager 对象;
  • 对于 Fragment , getFragmentManager() 是获取的父 Fragment (如果没有,则是 FragmentActivity )的 FragmentManager 对象,而 getChildFragmentManager() 是获取自身的 FragmentManager 对象。

FragmentManagerViewModel 介绍

每个 Fragment 创建时,都会创建一个 FragmentManagerViewModel 对象,在该对象中主要存储其 子Fragment 的 ViewModelStore 与 FragmentManagerViewMoel。具体结构如下所示:

2824145-2595c4fa9ec443d2.png
FragmentManagerViewModel.png

在 FragmentManagerViewModel 中:

  • mViewModelStore 是类型为 <String, FragmentManagerViewModel> 的 HashMap
  • mChildNonConfigs 是类型为 <String, ViewModelStore> 的 HashMap

上述两个 Map 对应的 Key 值都为 Fragment 的唯一 UUID。该 UUID 会在 Fragment 对象创建时自动生成。也就是每个 Fragment 对应唯一 UUID。

ViewModel 在 Fragment 绑定具体流程

ViewModel 与 Fragment 的绑定流程比较复杂,主要分为三个流程:

  • 第一步:在宿主 Activity 创建时,默认会在其 FramgentManager 中创建一个 FragmentManagerViewModel。同时将生成的 FragmentManagerViewModel 存储在其本身的 ViewModelStore 中。同时使用自身的FragmentManager
  • 第二步:在 Fragment 创建时,从 宿主Activity父Fragment 中的 FramgentManager 中获取对应的 FragmentManagerViewModel,并使用自身的 ChildFragmentManagermNonConfig 变量进行保存。
  • 第三步:将 Fragment 中所创建的 ViewModel 与其自身的 ViewModelStore 关联 ,并自身的 ViewModelStore 存储在 mNonConfig 所指向的 FragmentManaerViewModel 中的 mViewModelStores 中。

下面我将结合源码对这三个流程进行详细的介绍。

第一步流程

在宿主 Activity 创建时,默认会在其 FramgentManager 中创建一个 FragmentManagerViewModel。同时将生成的 FragmentManagerViewModel 存储在其本身的 ViewModelStore 中。同时使用自身的FragmentManager

FragmentActivity 中的 onCreate 方法:

    protected void onCreate(@Nullable Bundle savedInstanceState){mFragments.attachHost(null /*parent*/);//👈传入null//省略更多...}

mFragments 是 FragmentController,内部通过 FragmentHostCallback 间接控制 FragmentManager。

该方法最终会执行 FragmentActivity 中 FragmentManager 的 attachController 方法:

 void attachController(@NonNull FragmentHostCallback<?> host,@NonNull FragmentContainer container, @Nullable final Fragment parent) {//省略更多...if (parent != null) {mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);} else if (host instanceof ViewModelStoreOwner) {//👇走这里ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);} else {mNonConfig = new FragmentManagerViewModel(false);}}

因为传入的 parent = null,且 Activity 默认实现了 ViewModelStoreOwner 接口,所以会获取 Activity 中的 ViewModelStore,接着调用 FragmentManagerViewModel 的 getInstance() 方法:

    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,FACTORY);return viewModelProvider.get(FragmentManagerViewModel.class);}

在该方法中,会创建 FragmentManagerViewModel,并将其添加到 Activity 中的 ViewModelStore 中。

整体流程如下所示:

2824145-ba55a5f0003eafbd.png
第一步流程.png

第二步流程

在 Fragment 创建时,从 宿主Activity父Fragment 中的 FramgentManager 中获取对应的 FragmentManagerViewModel,并使用自身的 ChildFragmentManagermNonConfig 变量进行保存。

当 Fragment 与 Activity 关联时,在其 performAttach() 方法中

    void performAttach() {//👇又会调用attachControllermChildFragmentManager.attachController(mHost, new FragmentContainer() {@Override@Nullablepublic View onFindViewById(int id) {if (mView == null) {throw new IllegalStateException("Fragment " + this + " does not have a view");}return mView.findViewById(id);}@Overridepublic boolean onHasView() {return (mView != null);}}, this);//👈注意这里的this传入的parent是当前Fragment//省略更多...}

该方法会调用 Fragment 中 ChildFragmentManager 中的 attachController 方法如下所:

 void attachController(@NonNull FragmentHostCallback<?> host,@NonNull FragmentContainer container, @Nullable final Fragment parent) {//省略更多...if (parent != null) {//👆因为parent为this,故我们会获取Activity的FragmentManagermNonConfig = parent.mFragmentManager.getChildNonConfig(parent);} else if (host instanceof ViewModelStoreOwner) {ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();mNonConfig = FragmentManagerViewModel.getInstance(a);} else {mNonConfig = new FragmentManagerViewModel(false);}}

注意,当 Fragment 是 子Fragment 时,parent.fragmentManager 的值为父Fragment 的 FragmentManager,否则为 Activity 中的 FragmentManager。

继续追踪 FragmentManager 下的 getChildNonConfig 方法:

  private FragmentManagerViewModel getChildNonConfig(Fragment f){return mNonConfig.getChildNonConfig(f);}

mNonConfig 本身为 FragmentManagerViewModel,我们继续跟踪:

  FragmentManagerViewModel getChildNonConfig(Fragment f){FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);if (childNonConfig == null) {//👇创建Fragment的FragmentManagerViewModelchildNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);mChildNonConfigs.put(f.mWho, childNonConfig);}return childNonConfig;}

在该方法中,会从 Activity 中的 FragmentManagerViewModel 中的 mChildNonConfigs 中获取 Fragment 的 FragmentManagerViewModel,如果有,直接返回。反之,存入mChildNonConfigs 中。

整体流程如下所示:

2824145-36b76d3f9f1dc342.png
第二步流程.png

第三步流程

将 Fragment 中所创建的 ViewModel 与其自身的 ViewModelStore 关联 ,并将该 ViewModelStore 存储在 mNonConfig 所指向的 FragmentManaerViewModel 中的 mViewModelStores 中。

在 Fragment 中,ViewModelStore 是通过其 FragmentManager 创建与获取的。具体代码如所示:

    public ViewModelStore getViewModelStore() {if (mFragmentManager == null) {throw new IllegalStateException("Can't access ViewModels from detached fragment");}return mFragmentManager.getViewModelStore(this);}

注意,当 Fragment 是 子Fragment 时,mFragmentManager 的值为 父Fragment 的 FragmentManager,否则为 Activity 中的 FragmentManager。

继续追踪 FragmentManager 下的 getChildNonConfig 方法:

  ViewModelStore getViewModelStore(@NonNull Fragment f) {return mNonConfig.getViewModelStore(f);}

mNonConfig 本身为 FragmentManagerViewModel,最终会走 getViewModelStore 方法。

  ViewModelStore getViewModelStore(@NonNull Fragment f) {ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);if (viewModelStore == null) {viewModelStore = new ViewModelStore();//将创建好的ViewStore,放入FragmentManagerViewModel中mViewModelStores.put(f.mWho, viewModelStore);}return viewModelStore;}

在该方法中最终会将 Fragment 的 ViewModelStore 存入 FragmentManagerViewModel 中的 mViewModelStores 集合中。

那么 Fragment 的创建并获取 ViewModel 的流程如下所示:

2824145-f62c68713accb39b.png
第三步流程.png

ViewModel 在 Fragment 中不会因配置改变而销毁的原理

ViewModel 在 Fragment 中不会因配置改变而销毁的原因其实是因为其声明的 ViewModel 是存储在 FragmentManagerViewModel 中的,而 FragmentManagerViewModel 是存储在宿主 Activity 中的 ViewModelStore 中,又因 Activity 中 ViewModelStore不会因配置改变而销毁,故 Fragment 中 ViewModel 也不会因配置改变而销毁。

当然在 Google 的代码实现中,也能很好的处理 Fragment 嵌套的情况。在下述例子中展示了 Fragment 嵌套下 ViewModel 存储的情况。

2824145-14f6e5170e06f123.png
Activity与Fragment嵌套.png

在上图中,我们在 Activity 中 分别添加了 Fragment A、B、C。并在 Fragment C 中有嵌套了 Fragment D、E、F。

结合本篇文章所讲解的知识,我们能得到如下结构:

2824145-b249d46861405b4f.jpg
嵌套下实际结构.jpg

从上图中,我们可以看出,当存在嵌套 Fragment 的情况下,ViewModel 总是以线性的结构进行存储。在这种结构下,就能让宿主 Activity 良好的统一管理与所有的 ViewModel。

ViewModel 能在 Fragment 中共享的原理

ViewModel 的另一大特性就是能在 Fragment 中共享数据。还是以上图例:

假如我们想 Fragment D 获取 Fragment A 中的数据,那么我们只有在 Activity 中的 ViewModelStore 下添加 ViewModel。只有这样,我们才能在不同 Fragment 中获取相同的数据。这也是为什么在 Fragment 中使用共享的 ViewModel 时,我们要在调用ViewModelProvider.of() 创建 ViewModel 时需要传入 getActivity() 的原因。

具体例子如下所示:

    public class SharedViewModel extends ViewModel {private final MutableLiveData<Item> selected = new MutableLiveData<Item>();public void select(Item item) {selected.setValue(item);}public LiveData<Item> getSelected() {return selected;}}public class FragmentA extends Fragment {private SharedViewModel model;public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//👇传入的是宿主Activitymodel = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);itemSelector.setOnClickListener(item -> {model.select(item);});}}public class FragmentD extends Fragment {public void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//👇传入的是宿主ActivitySharedViewModel model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);model.getSelected().observe(this, { item ->// Update the UI.});}}

最后

站在巨人的肩膀上,才能看的更远~

  • ViewModel:持久化、onSaveInstanceState()、UI 状态恢复和 Loader
  • Fragment全解析系列(二):正确的使用姿势
  • 在 Fragment 之间共享数据
  • Handling Configuration Changes with Fragments
查看全文
如若内容造成侵权/违法违规/事实不符,请联系编程学习网邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!

相关文章

  1. ajaxpro使用方法以及发布到服务器出现的问题修改方法

    后台方法[AjaxPro.AjaxNamespace("UserTypeshow")]//自己定义 public partial class UC_BusinessUC_MgtUser_MgtUser : System.Web.UI.UserControl {public string strTag = "";protected void Page_Load(object sender, EventArgs e){AjaxPro.Utility.Reg…...

    2024/4/12 13:46:44
  2. 易语言API模块制作PathFindExtensionA文件取扩展名

    PathFindExtensionA是windows用来获取绝对路径或文件名中的文件扩展名(文件名后缀)的一个函数。 易语言PathFindExtensionA获取文件扩展名视频链接:511遇见易语言模块API教程API-PathFindExtensionAPTSTR PathFindExtension( _In_ PTSTR pszPath//指向以0结尾,最大长度为MA…...

    2024/4/10 8:27:20
  3. 【Unity3D游戏开发】App游戏名字本地化 (二七)

    开发环境Unity5.3 XCode7.2目标游戏名字需要多语言处理过程1.添加多语言 2.用CFBundleDisplayName来处理名字多语言 失败尝试流程1.未成功 info.plist里, 添加Application has localized display name ,并设置成yes2.InfoPlist.Strings要选对工程 扩展如果你想为不同的设备(i…...

    2024/5/5 18:23:13
  4. 小甲鱼Python第六讲课后题

    小甲鱼Python第六讲课后题 1.Python 的 floor 除法现在使用 “//” 实现,那 3.0 // 2.0 您目测会显示什么内容呢? 答案:1.0,Python会严格执行floor除法; 2. a< b < c 事实上是等于? 答案:(a < b) and (b < c) 3.不使用 IDLE,你可以轻松说出 5 ** -2 的值吗…...

    2024/4/12 6:21:57
  5. AjaxPro V.6.2.16.1使用数组报错

    在这Ajax风靡日子里,忍不住试用了一下新版的AjaxPro (AssemblyVersion: 6.2.16.1),谁知一测没点反应,Server端代码如下:[AjaxPro.AjaxMethod] public object[] GetNumbers(int[] a, double[] b) { object[] x = new object[2]; x[0] = …...

    2024/5/10 8:39:25
  6. 基于Android官方AsyncListUtil优化经典ListView分页加载机制(二)

    基于Android官方AsyncListUtil优化经典ListView分页加载机制(二)我写的附录文章1,介绍了如何使用Android官方的分页加载框架AsyncListUtil优化改进常见的RecyclerView分页加载实现。AsyncListUtil作为一种通用的分页加载框架,不仅可以套用在RecyclerView,也可也适用在经典…...

    2024/5/10 5:33:55
  7. (票据/标签)打印机编程手册命令原理详细解析

    最近在公司使用票据打印机编程手册指令对成品进行测试检测,对打印命令需要深入理解,才能更好掌握它的原理和使用。 一、票据打印编程手册 相关打印机的打印指令/编程手册可以在网络上找到茫茫多的资源。像类似LF ESC开头的,并不是代表字符串,他们都是ASCII码表里排名靠前…...

    2024/4/24 19:26:30
  8. 双感叹号!!运算符的用法

    因为C语言中,所有非0值都表示“真”。所以!非0值 = 0,而!0 = 1。双感叹号!!作用就是非0值转成1,而0值还是0.双感叹号!!是为了把"非0值"转换成1,而0值还是0。!!不难理解,举例:!!(expression) 等价于!(!expression)。如果express是一个指针,最终结果就是1.这样!…...

    2024/4/12 13:53:15
  9. 知识图谱-安装Virtuoso和导入ttl数据

    1.开源版本的virtuoso数据库(以7.2.5为例),下载位置https://sourceforge.net/projects/virtuoso/files/virtuoso/2.安装(1)下载并解压到/usr/local下,tar -zxvf virtuoso-opensource.x86_64-generic_glibc25-linux-gnu.tar.gz(2)修改/etc/profile,添加export VIRTUOSO_HOME=/…...

    2024/5/6 8:46:53
  10. 小甲鱼python零基础课后习题001我和Python的第一次亲密接触

    一、测试题:0. python是什么语言?1. IDLE是什么?2. print()的作用是什么?3. python中表示乘法的符号是什么?4. 为什么print("i love you" + 5)不可以执行会报错,而print("i love you" * 5)可以正常执行?二、动动手:0. 如何在字符穿中嵌入双引…...

    2024/5/6 5:58:35
  11. vue filter的四种用法

    本章主要讲vue2的过滤器的使用先介绍下vue1与vue2的filter区别,也就是vue2更新的地方a: 2.0将1.0所有自带的过滤器都删除了,也就是说,在2.0中,要使用过滤器,则需要我们自己编写。b: 2.0过滤器的传参方式不是以前的方式,是以函数传参的方式,下面示例(后面有具体示例):…...

    2024/4/22 18:11:18
  12. Android Butterknife 框架源码解析(1)——ButterKnife的使用

    ButterKnife是目前常用的一种依托Java注解机制实现辅助代码生成的框架,有了它,妈妈再也不用担心我写大量枯燥的findViewById以及OnXXXListener响应事件了,一行代码就搞定,自从接触它以后我就再也离不开它了。既然如此,我也就抽个时间,好好研究了一下它,总结一下它的使用…...

    2024/4/5 0:16:18
  13. 链表之反转链表专题

    链表之反转链表专题 如题,LeetCode 206看到反转链表,我们要思考的核心点在于如何让链表的结点指针指向的方向反转 对于反转链表,一般有迭代或者递归两种思考方向 第一种:迭代 在这种方案中,做题总结出了双指针迭代比较高效,同时有一种双指针的迭代公式 具体的动画效果可以…...

    2024/4/12 6:21:45
  14. 小甲鱼python课后习题【31,32,33,34,35】

    【学习笔记,仅供学习交流使用,知识源于鱼c论坛】 作业31: 测试题: 0、pickle的实质是什么?a.将对象数据以二进制形式保存在文件中b.将对象数据以二进制形式从文件中读取输入输出二进制流 1、使用pickle的什么方法存储数据?dump() 2、使用pickle的什么方法读取数据?load(…...

    2024/4/12 20:03:14
  15. ANTLR4权威参考手册(二)

    第一部分ANTLR和计算机语言在第一部分中,我们会学习ANTLR安装,尝试通过一个简单的“HelloWorld”语法,并了解语言应用程序开发大纲。有了这些基础,我们将建立一个语法去识别和翻译大括号中的整数列表,如{1,2,3}。最后,我们将通过一些ANTLR功能简单的语法及应用去开启对an…...

    2024/5/2 8:07:45
  16. ToStringBuilder.reflectionToString用法

    先上用例: /*** 重写对象toString方法** @return String [field=value]...*/public String toString() {return ToStringBuilder.reflectionToString(this, ToStringStyle.JSON_STYLE);}我们都知道String、StringBuffer、StringBuilder的区别(不明白的可以看下面) Java平台提…...

    2024/4/12 21:50:26
  17. IOS游戏名字的本地化修改

    http://blog.csdn.net/tangaowen/article/details/8878126...

    2024/5/2 13:59:33
  18. 侧滑

    侧滑简单实现1.找到布局DrawerLayout控件DrawerLayout drawer = findViewById(R.id.drawer); 2.设置支持侧滑getSupportActionBar().setDisplayHomeAsUpEnabled(true); 3.获取fragment---id并给侧滑页面添加布局getSupportFragmentManager().beginTransaction().replace(R.id.l…...

    2024/5/2 10:38:40
  19. ajaxpro 异步调用

    AjaxPro一般默认是同步调用,异步调用只需要在方法后面加一个callback函数,直接取value属性即可。例如:MyNameSpace.Page1.getOtherConfig("AutoSubmitMode",function(result){ autoSubmitMode=result.value; });转载于:https://www.cnblogs.com/nanfei/p/5210766…...

    2024/4/18 15:17:44
  20. 转载mark-反调试技术

    这里分析的是6月18号的版本,由于某些原因这里隐去敏感内容目前静态分析跟踪到7个系统线程创建,线程创建过程如下:先判断hv是否存在如果hv不存在则搜索SeSetAuditParameter中的FF E1特征码根据如果成功搜索到上述fake thread entry则把线程入口指向上述entry,线程参数指向原…...

    2024/5/2 21:11:03

最新文章

  1. Redis未授权访问

    一、漏洞描述 Redis未授权访问 因配置不当可以未经授权访问&#xff0c;攻击者无需认证就可以访问到内部数据。 1. 导致敏感信息泄露 2. 执行 flushall 可清空所有数据 3. 通过数据备份功能往磁盘写入后门文件&#xff08;webshell、定时任务&#xff09; 4. 如果Redis以…...

    2024/5/10 10:50:02
  2. 梯度消失和梯度爆炸的一些处理方法

    在这里是记录一下梯度消失或梯度爆炸的一些处理技巧。全当学习总结了如有错误还请留言&#xff0c;在此感激不尽。 权重和梯度的更新公式如下&#xff1a; w w − η ⋅ ∇ w w w - \eta \cdot \nabla w ww−η⋅∇w 个人通俗的理解梯度消失就是网络模型在反向求导的时候出…...

    2024/5/9 21:23:04
  3. FreeRTOS学习 -- 再识

    工作中一直使用FreeRTOS进行着开发&#xff0c;但是没有进行过系统的总结过。现在将快速使用几天时间将FreeRTOS相关知识点加以总结。 官网&#xff1a; https://www.freertos.org/zh-cn-cmn-s/ 参看资料&#xff1a; 正点原子 STM32F1 FreeRTOS开发手册_V1.2.pdf The FreeRTOS…...

    2024/5/9 9:38:57
  4. app上架-您的应用存在最近任务列表隐藏风险活动的行为,不符合华为应用市场审核标准。

    上架提示 您的应用存在最近任务列表隐藏风险活动的行为&#xff0c;不符合华为应用市场审核标准。 修改建议&#xff1a;请参考测试结果进行修改。 请参考《审核指南》第2.19相关审核要求&#xff1a;https://developer.huawei.com/consumer/cn/doc/app/50104-02 造成原因 …...

    2024/5/10 6:49:11
  5. 【外汇早评】美通胀数据走低,美元调整

    原标题:【外汇早评】美通胀数据走低,美元调整昨日美国方面公布了新一期的核心PCE物价指数数据,同比增长1.6%,低于前值和预期值的1.7%,距离美联储的通胀目标2%继续走低,通胀压力较低,且此前美国一季度GDP初值中的消费部分下滑明显,因此市场对美联储后续更可能降息的政策…...

    2024/5/8 6:01:22
  6. 【原油贵金属周评】原油多头拥挤,价格调整

    原标题:【原油贵金属周评】原油多头拥挤,价格调整本周国际劳动节,我们喜迎四天假期,但是整个金融市场确实流动性充沛,大事频发,各个商品波动剧烈。美国方面,在本周四凌晨公布5月份的利率决议和新闻发布会,维持联邦基金利率在2.25%-2.50%不变,符合市场预期。同时美联储…...

    2024/5/9 15:10:32
  7. 【外汇周评】靓丽非农不及疲软通胀影响

    原标题:【外汇周评】靓丽非农不及疲软通胀影响在刚结束的周五,美国方面公布了新一期的非农就业数据,大幅好于前值和预期,新增就业重新回到20万以上。具体数据: 美国4月非农就业人口变动 26.3万人,预期 19万人,前值 19.6万人。 美国4月失业率 3.6%,预期 3.8%,前值 3…...

    2024/5/4 23:54:56
  8. 【原油贵金属早评】库存继续增加,油价收跌

    原标题:【原油贵金属早评】库存继续增加,油价收跌周三清晨公布美国当周API原油库存数据,上周原油库存增加281万桶至4.692亿桶,增幅超过预期的74.4万桶。且有消息人士称,沙特阿美据悉将于6月向亚洲炼油厂额外出售更多原油,印度炼油商预计将每日获得至多20万桶的额外原油供…...

    2024/5/9 4:20:59
  9. 【外汇早评】日本央行会议纪要不改日元强势

    原标题:【外汇早评】日本央行会议纪要不改日元强势近两日日元大幅走强与近期市场风险情绪上升,避险资金回流日元有关,也与前一段时间的美日贸易谈判给日本缓冲期,日本方面对汇率问题也避免继续贬值有关。虽然今日早间日本央行公布的利率会议纪要仍然是支持宽松政策,但这符…...

    2024/5/4 23:54:56
  10. 【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响

    原标题:【原油贵金属早评】欧佩克稳定市场,填补伊朗问题的影响近日伊朗局势升温,导致市场担忧影响原油供给,油价试图反弹。此时OPEC表态稳定市场。据消息人士透露,沙特6月石油出口料将低于700万桶/日,沙特已经收到石油消费国提出的6月份扩大出口的“适度要求”,沙特将满…...

    2024/5/4 23:55:05
  11. 【外汇早评】美欲与伊朗重谈协议

    原标题:【外汇早评】美欲与伊朗重谈协议美国对伊朗的制裁遭到伊朗的抗议,昨日伊朗方面提出将部分退出伊核协议。而此行为又遭到欧洲方面对伊朗的谴责和警告,伊朗外长昨日回应称,欧洲国家履行它们的义务,伊核协议就能保证存续。据传闻伊朗的导弹已经对准了以色列和美国的航…...

    2024/5/4 23:54:56
  12. 【原油贵金属早评】波动率飙升,市场情绪动荡

    原标题:【原油贵金属早评】波动率飙升,市场情绪动荡因中美贸易谈判不安情绪影响,金融市场各资产品种出现明显的波动。随着美国与中方开启第十一轮谈判之际,美国按照既定计划向中国2000亿商品征收25%的关税,市场情绪有所平复,已经开始接受这一事实。虽然波动率-恐慌指数VI…...

    2024/5/7 11:36:39
  13. 【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试

    原标题:【原油贵金属周评】伊朗局势升温,黄金多头跃跃欲试美国和伊朗的局势继续升温,市场风险情绪上升,避险黄金有向上突破阻力的迹象。原油方面稍显平稳,近期美国和OPEC加大供给及市场需求回落的影响,伊朗局势并未推升油价走强。近期中美贸易谈判摩擦再度升级,美国对中…...

    2024/5/4 23:54:56
  14. 【原油贵金属早评】市场情绪继续恶化,黄金上破

    原标题:【原油贵金属早评】市场情绪继续恶化,黄金上破周初中国针对于美国加征关税的进行的反制措施引发市场情绪的大幅波动,人民币汇率出现大幅的贬值动能,金融市场受到非常明显的冲击。尤其是波动率起来之后,对于股市的表现尤其不安。隔夜美国股市出现明显的下行走势,这…...

    2024/5/6 1:40:42
  15. 【外汇早评】美伊僵持,风险情绪继续升温

    原标题:【外汇早评】美伊僵持,风险情绪继续升温昨日沙特两艘油轮再次发生爆炸事件,导致波斯湾局势进一步恶化,市场担忧美伊可能会出现摩擦生火,避险品种获得支撑,黄金和日元大幅走强。美指受中美贸易问题影响而在低位震荡。继5月12日,四艘商船在阿联酋领海附近的阿曼湾、…...

    2024/5/4 23:54:56
  16. 【原油贵金属早评】贸易冲突导致需求低迷,油价弱势

    原标题:【原油贵金属早评】贸易冲突导致需求低迷,油价弱势近日虽然伊朗局势升温,中东地区几起油船被袭击事件影响,但油价并未走高,而是出于调整结构中。由于市场预期局势失控的可能性较低,而中美贸易问题导致的全球经济衰退风险更大,需求会持续低迷,因此油价调整压力较…...

    2024/5/8 20:48:49
  17. 氧生福地 玩美北湖(上)——为时光守候两千年

    原标题:氧生福地 玩美北湖(上)——为时光守候两千年一次说走就走的旅行,只有一张高铁票的距离~ 所以,湖南郴州,我来了~ 从广州南站出发,一个半小时就到达郴州西站了。在动车上,同时改票的南风兄和我居然被分到了一个车厢,所以一路非常愉快地聊了过来。 挺好,最起…...

    2024/5/7 9:26:26
  18. 氧生福地 玩美北湖(中)——永春梯田里的美与鲜

    原标题:氧生福地 玩美北湖(中)——永春梯田里的美与鲜一觉醒来,因为大家太爱“美”照,在柳毅山庄去寻找龙女而错过了早餐时间。近十点,向导坏坏还是带着饥肠辘辘的我们去吃郴州最富有盛名的“鱼头粉”。说这是“十二分推荐”,到郴州必吃的美食之一。 哇塞!那个味美香甜…...

    2024/5/4 23:54:56
  19. 氧生福地 玩美北湖(下)——奔跑吧骚年!

    原标题:氧生福地 玩美北湖(下)——奔跑吧骚年!让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 让我们红尘做伴 活得潇潇洒洒 策马奔腾共享人世繁华 对酒当歌唱出心中喜悦 轰轰烈烈把握青春年华 啊……啊……啊 两…...

    2024/5/8 19:33:07
  20. 扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!

    原标题:扒开伪装医用面膜,翻六倍价格宰客,小姐姐注意了!扒开伪装医用面膜,翻六倍价格宰客!当行业里的某一品项火爆了,就会有很多商家蹭热度,装逼忽悠,最近火爆朋友圈的医用面膜,被沾上了污点,到底怎么回事呢? “比普通面膜安全、效果好!痘痘、痘印、敏感肌都能用…...

    2024/5/5 8:13:33
  21. 「发现」铁皮石斛仙草之神奇功效用于医用面膜

    原标题:「发现」铁皮石斛仙草之神奇功效用于医用面膜丽彦妆铁皮石斛医用面膜|石斛多糖无菌修护补水贴19大优势: 1、铁皮石斛:自唐宋以来,一直被列为皇室贡品,铁皮石斛生于海拔1600米的悬崖峭壁之上,繁殖力差,产量极低,所以古代仅供皇室、贵族享用 2、铁皮石斛自古民间…...

    2024/5/8 20:38:49
  22. 丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者

    原标题:丽彦妆\医用面膜\冷敷贴轻奢医学护肤引导者【公司简介】 广州华彬企业隶属香港华彬集团有限公司,专注美业21年,其旗下品牌: 「圣茵美」私密荷尔蒙抗衰,产后修复 「圣仪轩」私密荷尔蒙抗衰,产后修复 「花茵莳」私密荷尔蒙抗衰,产后修复 「丽彦妆」专注医学护…...

    2024/5/4 23:54:58
  23. 广州械字号面膜生产厂家OEM/ODM4项须知!

    原标题:广州械字号面膜生产厂家OEM/ODM4项须知!广州械字号面膜生产厂家OEM/ODM流程及注意事项解读: 械字号医用面膜,其实在我国并没有严格的定义,通常我们说的医美面膜指的应该是一种「医用敷料」,也就是说,医用面膜其实算作「医疗器械」的一种,又称「医用冷敷贴」。 …...

    2024/5/10 10:22:18
  24. 械字号医用眼膜缓解用眼过度到底有无作用?

    原标题:械字号医用眼膜缓解用眼过度到底有无作用?医用眼膜/械字号眼膜/医用冷敷眼贴 凝胶层为亲水高分子材料,含70%以上的水分。体表皮肤温度传导到本产品的凝胶层,热量被凝胶内水分子吸收,通过水分的蒸发带走大量的热量,可迅速地降低体表皮肤局部温度,减轻局部皮肤的灼…...

    2024/5/9 17:11:10
  25. 配置失败还原请勿关闭计算机,电脑开机屏幕上面显示,配置失败还原更改 请勿关闭计算机 开不了机 这个问题怎么办...

    解析如下&#xff1a;1、长按电脑电源键直至关机&#xff0c;然后再按一次电源健重启电脑&#xff0c;按F8健进入安全模式2、安全模式下进入Windows系统桌面后&#xff0c;按住“winR”打开运行窗口&#xff0c;输入“services.msc”打开服务设置3、在服务界面&#xff0c;选中…...

    2022/11/19 21:17:18
  26. 错误使用 reshape要执行 RESHAPE,请勿更改元素数目。

    %读入6幅图像&#xff08;每一幅图像的大小是564*564&#xff09; f1 imread(WashingtonDC_Band1_564.tif); subplot(3,2,1),imshow(f1); f2 imread(WashingtonDC_Band2_564.tif); subplot(3,2,2),imshow(f2); f3 imread(WashingtonDC_Band3_564.tif); subplot(3,2,3),imsho…...

    2022/11/19 21:17:16
  27. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机...

    win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”问题的解决方法在win7系统关机时如果有升级系统的或者其他需要会直接进入一个 等待界面&#xff0c;在等待界面中我们需要等待操作结束才能关机&#xff0c;虽然这比较麻烦&#xff0c;但是对系统进行配置和升级…...

    2022/11/19 21:17:15
  28. 台式电脑显示配置100%请勿关闭计算机,“准备配置windows 请勿关闭计算机”的解决方法...

    有不少用户在重装Win7系统或更新系统后会遇到“准备配置windows&#xff0c;请勿关闭计算机”的提示&#xff0c;要过很久才能进入系统&#xff0c;有的用户甚至几个小时也无法进入&#xff0c;下面就教大家这个问题的解决方法。第一种方法&#xff1a;我们首先在左下角的“开始…...

    2022/11/19 21:17:14
  29. win7 正在配置 请勿关闭计算机,怎么办Win7开机显示正在配置Windows Update请勿关机...

    置信有很多用户都跟小编一样遇到过这样的问题&#xff0c;电脑时发现开机屏幕显现“正在配置Windows Update&#xff0c;请勿关机”(如下图所示)&#xff0c;而且还需求等大约5分钟才干进入系统。这是怎样回事呢&#xff1f;一切都是正常操作的&#xff0c;为什么开时机呈现“正…...

    2022/11/19 21:17:13
  30. 准备配置windows 请勿关闭计算机 蓝屏,Win7开机总是出现提示“配置Windows请勿关机”...

    Win7系统开机启动时总是出现“配置Windows请勿关机”的提示&#xff0c;没过几秒后电脑自动重启&#xff0c;每次开机都这样无法进入系统&#xff0c;此时碰到这种现象的用户就可以使用以下5种方法解决问题。方法一&#xff1a;开机按下F8&#xff0c;在出现的Windows高级启动选…...

    2022/11/19 21:17:12
  31. 准备windows请勿关闭计算机要多久,windows10系统提示正在准备windows请勿关闭计算机怎么办...

    有不少windows10系统用户反映说碰到这样一个情况&#xff0c;就是电脑提示正在准备windows请勿关闭计算机&#xff0c;碰到这样的问题该怎么解决呢&#xff0c;现在小编就给大家分享一下windows10系统提示正在准备windows请勿关闭计算机的具体第一种方法&#xff1a;1、2、依次…...

    2022/11/19 21:17:11
  32. 配置 已完成 请勿关闭计算机,win7系统关机提示“配置Windows Update已完成30%请勿关闭计算机”的解决方法...

    今天和大家分享一下win7系统重装了Win7旗舰版系统后&#xff0c;每次关机的时候桌面上都会显示一个“配置Windows Update的界面&#xff0c;提示请勿关闭计算机”&#xff0c;每次停留好几分钟才能正常关机&#xff0c;导致什么情况引起的呢&#xff1f;出现配置Windows Update…...

    2022/11/19 21:17:10
  33. 电脑桌面一直是清理请关闭计算机,windows7一直卡在清理 请勿关闭计算机-win7清理请勿关机,win7配置更新35%不动...

    只能是等着&#xff0c;别无他法。说是卡着如果你看硬盘灯应该在读写。如果从 Win 10 无法正常回滚&#xff0c;只能是考虑备份数据后重装系统了。解决来方案一&#xff1a;管理员运行cmd&#xff1a;net stop WuAuServcd %windir%ren SoftwareDistribution SDoldnet start WuA…...

    2022/11/19 21:17:09
  34. 计算机配置更新不起,电脑提示“配置Windows Update请勿关闭计算机”怎么办?

    原标题&#xff1a;电脑提示“配置Windows Update请勿关闭计算机”怎么办&#xff1f;win7系统中在开机与关闭的时候总是显示“配置windows update请勿关闭计算机”相信有不少朋友都曾遇到过一次两次还能忍但经常遇到就叫人感到心烦了遇到这种问题怎么办呢&#xff1f;一般的方…...

    2022/11/19 21:17:08
  35. 计算机正在配置无法关机,关机提示 windows7 正在配置windows 请勿关闭计算机 ,然后等了一晚上也没有关掉。现在电脑无法正常关机...

    关机提示 windows7 正在配置windows 请勿关闭计算机 &#xff0c;然后等了一晚上也没有关掉。现在电脑无法正常关机以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;关机提示 windows7 正在配…...

    2022/11/19 21:17:05
  36. 钉钉提示请勿通过开发者调试模式_钉钉请勿通过开发者调试模式是真的吗好不好用...

    钉钉请勿通过开发者调试模式是真的吗好不好用 更新时间:2020-04-20 22:24:19 浏览次数:729次 区域: 南阳 > 卧龙 列举网提醒您:为保障您的权益,请不要提前支付任何费用! 虚拟位置外设器!!轨迹模拟&虚拟位置外设神器 专业用于:钉钉,外勤365,红圈通,企业微信和…...

    2022/11/19 21:17:05
  37. 配置失败还原请勿关闭计算机怎么办,win7系统出现“配置windows update失败 还原更改 请勿关闭计算机”,长时间没反应,无法进入系统的解决方案...

    前几天班里有位学生电脑(windows 7系统)出问题了&#xff0c;具体表现是开机时一直停留在“配置windows update失败 还原更改 请勿关闭计算机”这个界面&#xff0c;长时间没反应&#xff0c;无法进入系统。这个问题原来帮其他同学也解决过&#xff0c;网上搜了不少资料&#x…...

    2022/11/19 21:17:04
  38. 一个电脑无法关闭计算机你应该怎么办,电脑显示“清理请勿关闭计算机”怎么办?...

    本文为你提供了3个有效解决电脑显示“清理请勿关闭计算机”问题的方法&#xff0c;并在最后教给你1种保护系统安全的好方法&#xff0c;一起来看看&#xff01;电脑出现“清理请勿关闭计算机”在Windows 7(SP1)和Windows Server 2008 R2 SP1中&#xff0c;添加了1个新功能在“磁…...

    2022/11/19 21:17:03
  39. 请勿关闭计算机还原更改要多久,电脑显示:配置windows更新失败,正在还原更改,请勿关闭计算机怎么办...

    许多用户在长期不使用电脑的时候&#xff0c;开启电脑发现电脑显示&#xff1a;配置windows更新失败&#xff0c;正在还原更改&#xff0c;请勿关闭计算机。。.这要怎么办呢&#xff1f;下面小编就带着大家一起看看吧&#xff01;如果能够正常进入系统&#xff0c;建议您暂时移…...

    2022/11/19 21:17:02
  40. 还原更改请勿关闭计算机 要多久,配置windows update失败 还原更改 请勿关闭计算机,电脑开机后一直显示以...

    配置windows update失败 还原更改 请勿关闭计算机&#xff0c;电脑开机后一直显示以以下文字资料是由(历史新知网www.lishixinzhi.com)小编为大家搜集整理后发布的内容&#xff0c;让我们赶快一起来看一下吧&#xff01;配置windows update失败 还原更改 请勿关闭计算机&#x…...

    2022/11/19 21:17:01
  41. 电脑配置中请勿关闭计算机怎么办,准备配置windows请勿关闭计算机一直显示怎么办【图解】...

    不知道大家有没有遇到过这样的一个问题&#xff0c;就是我们的win7系统在关机的时候&#xff0c;总是喜欢显示“准备配置windows&#xff0c;请勿关机”这样的一个页面&#xff0c;没有什么大碍&#xff0c;但是如果一直等着的话就要两个小时甚至更久都关不了机&#xff0c;非常…...

    2022/11/19 21:17:00
  42. 正在准备配置请勿关闭计算机,正在准备配置windows请勿关闭计算机时间长了解决教程...

    当电脑出现正在准备配置windows请勿关闭计算机时&#xff0c;一般是您正对windows进行升级&#xff0c;但是这个要是长时间没有反应&#xff0c;我们不能再傻等下去了。可能是电脑出了别的问题了&#xff0c;来看看教程的说法。正在准备配置windows请勿关闭计算机时间长了方法一…...

    2022/11/19 21:16:59
  43. 配置失败还原请勿关闭计算机,配置Windows Update失败,还原更改请勿关闭计算机...

    我们使用电脑的过程中有时会遇到这种情况&#xff0c;当我们打开电脑之后&#xff0c;发现一直停留在一个界面&#xff1a;“配置Windows Update失败&#xff0c;还原更改请勿关闭计算机”&#xff0c;等了许久还是无法进入系统。如果我们遇到此类问题应该如何解决呢&#xff0…...

    2022/11/19 21:16:58
  44. 如何在iPhone上关闭“请勿打扰”

    Apple’s “Do Not Disturb While Driving” is a potentially lifesaving iPhone feature, but it doesn’t always turn on automatically at the appropriate time. For example, you might be a passenger in a moving car, but your iPhone may think you’re the one dri…...

    2022/11/19 21:16:57