Jetpack ---- ViewModel源码解析

本文介绍了Android Jetpack中的ViewModel,详细分析了其生命周期、优势和使用方法。ViewModel在配置更改时能保持数据不丢失,简化数据共享,且在Activity重建后仍能保留数据。文章还探讨了ViewModel的源码,包括Fragment和Activity中的流程,以及如何确保数据在页面配置更改时不丢失的机制。
摘要由CSDN通过智能技术生成

这篇博客参考了 程序亦非猿heiylHankkin

1.背景

上篇我们对LiveData进行了分析,已清楚了它的主要作用,我们再来温习一下:

LiveData是一个可以感知Activity、Fragment生命周期的数据容器。其本身是基于观察者模式设计的,当LiveData所持有的数据发生改变时,它会通知对应的界面所持有该数据的UI进行更新,并且LiveData中持有Lifecycle的引用,所以只会在LifecycleOwner处于Active的状态下通知数据改变,果数据改变发生在非 active 状态,数据会变化,但是不发送通知,等 owner 回到 active 的状态下,再发送通知.

而LiveData配合今天所讲的ViewModel使用起来真的是非常方便,接下来我们开始分析:

2.简介

2.1 是什么?

简单来说:ViewModel是以关联生命周期的方式来存储和管理UI相关数据的类,即使configuration发生改变(例如屏幕旋转),数据仍然可以存在不会销毁.

举个例子来说:我们在开发中经常会遇到这种场景,当我们的Activity和Fragment被销毁重建之后,它们中的数据将会丢失,而我们一般的解决方案就是通过onSaveInstanceState()中保存数据,然后在onCreate()中恢复过来,但是当这些数据较大时或者数据可能又需要重新请求接口,这时候ViewModel就派上用场了,也就是说当我们把数据保存在ViewModel中,不管Activity和Fragment被销毁了还是屏幕旋转导致configuration发生了变化,保存在其中的数据依然存在。

不仅如此,ViewModel还可以帮助我们轻易的实现Fragment及Activity之间的数据共享和通信,下面会详细介绍。

2.2 ViewModel生命周期

先来看一下官方给出的图:
在这里插入图片描述
图中展示了当一个Activity经过屏幕旋转后的生命周期状态改变,右侧则是ViewModel的生命周期状态。我们可以看到ViewModel的生命周期并不会跟随着Activity的生命周期变化而变化,虽然ViewModel是由该Activity创建的。我们会在源码分析部分再去看ViewModel的生命周期具体是怎样的。下面我们看下ViewModel该怎样使用?

2.3 优势

页面配置更改数据不丢失

  • 当设备因配置更改导致 Activity/Fragment 重建,ViewModel 中的数据并不会因此而丢失,配合 LiveData 可以在页面重建后立马能收到最新保存的数据用以重新渲染页面。

Android的Activity的生命周期有很多状态,并且由于配置更改,单个Activity可能会在这些不同状态之间循环多次。参照下面Activity生命周期图:
在这里插入图片描述
当一个Activity经历了所有这些状态,您可能还需要将瞬态UI数据保存在内存中。我将瞬态UI数据定义为UI所需的数据,包括用户输入的数据,运行时生成的数据或从数据库加载的数据。这些数据可以是图片位图、用于RecyclerView的对象列表等。

如,用户旋转屏幕时,以前我们通过onSaveInstanceState()和onRestoreInstanceState()在配置更改期间保存或还原此数据。

用户旋转屏幕时如下图:当系统开始停止您的Activity时:

  1. 它会调用onSaveInstanceState(),以便您可以指定要保存的其他状态数据,以防Activity必须重新创建实例。
  2. 如果Activity被破坏并且必须重新创建相同的实例,则系统将(1)中定义的状态数据传递给onCreate()方法和onRestoreInstanceState()方法。
    在这里插入图片描述

但是,如果您的数据不需要知道或管理“Activity”所处于的生命周期状态,或者不将其存储在Activity内该怎么做呢?这就是ViewModel类的目的。

ViewModel 和 onSaveIntanceState 方法区别

  • onSaveIntanceState 只能存储轻量级的 key-value 键值对数据,非配置变更导致的页面被回收时才会触发,此时数据存储在 ActivityRecord 中;
  • ViewModel 可以存放任意 Object 数据,因配置变更导致的页面被回收才有效。此时存在ActivityThread#ActivityClientRecord 中。

生命周期感应

  • 在 ViewModel 中难免会做一些网络请求或数据的处理,可以复写 onCleared() 方法,终止清理一些操作,释放内存。该方法在宿主 onDestroy 时被调用

如下图:

你可以看到Activity的生命周期在旋转过程中的状态流转直到finish。ViewModel的生命周期显示在关联的Activity的生命周期的旁边。请注意,这个ViewModels可以简单轻松地与Activities/Fragments结合使用。
在这里插入图片描述
您通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。系统可能会在 Activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。ViewModel 存在的时间范围是从您首次请求 ViewModel 直到 Activity 完成并销毁

数据共享

对于单 Activity 对 Fragment 的页面,可以使用 ViewModel 实现页面之间的数据共享,实际上不同的 Activity也可以实现数据共享。

Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的情况。

想象一下主从 Fragment 的常见情况,假设您有一个 Fragment,在该 Fragment 中,用户从列表中选择一项,还有另一个 Fragment,用于显示选定项的内容。

这种情况不太容易处理,因为这两个 Fragment 都需要定义某种接口描述,并且所有者 Activity 必须将两者绑定在一起。

此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。

可以使用 ViewModel 对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel 来处理此类通信,如以下示例代码所示:

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 MasterFragment extends Fragment {
    private SharedViewModel model;

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //!!!请注意:这两个 Fragment 都会检索包含它们的 Activity
        model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        itemSelector.setOnClickListener(item -> {
            model.select(item);
        });
    }
}

public class DetailFragment extends Fragment {

    public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        //!!!请注意:这两个 Fragment 都会检索包含它们的 Activity
        SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
        model.getSelected().observe(getViewLifecycleOwner(), { item ->
           // Update the UI.
        });
    }
}

通过ViewModel在 Fragment 之间共享数据的优势:

  1. Activity 不需要执行任何操作,也不需要对此通信有任何了解。
  2. 除了 SharedViewModel 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
  3. 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。

3. 基本使用

ViewModel 的使用也非常简单,Android 提供了一个 ViewModel 类让我们去继承,并且提供了 ViewModelProviders 来帮助我们实例化 ViewModel。

LiveData , ViewModel 组件都是基于Lifecycle来实现的,使用 ViewModel 之前需要先添加依赖:

声明依赖项

dependencies {
        def lifecycle_version = "1.1.1"

        // ViewModel
        implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version"
}

demo

1. 自定义一个MyViewModel 继承自ViewModel,并且包含了一个 LiveData:

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<User>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}

2. 在 Activity 中借助 ViewModelProviders 获得 ViewModel 的实例,并借助 LiveData 订阅 users 的变化通知

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        // Create a ViewModel the first time the system calls an activity's onCreate() method.
        // Re-created activities receive the same MyViewModel instance created by the first activity.

        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

就这样简单的步骤,我们就使用上了 ViewModel,即便 MyActivity 重新创建,MyActivity 拿到的 MyViewModel 都会是一个实例。

总结

使用ViewModel的三个步骤:

  1. 通过创建一个继承自ViewModel的类来从你的UI控制器(Activity或Fragment)中分离出你的ViewModel
  2. 在你的UI控制器和ViewModel之间建立通讯
  3. 在你的UI控制器中使用ViewModel

1. 创建一个ViewModel类

引用官方:一个非常简单的例子

  • 通常情况下,你需要为你的app的每个页面创建一个ViewModel类。
  • 这个ViewModel类会掌控和管理所有的与页面相关数据,并且提供get/set方法用于存取数据。
  • 这会将显示Activity的UI代码与您的用于显示UI的数据分离开(显示UI的数据现在位于ViewModel中)。
public class ScoreViewModel extends ViewModel {
   // Tracks the score for Team A
   public int scoreTeamA = 0;

   // Tracks the score for Team B
   public int scoreTeamB = 0;
}

2. 关联UI控制器和ViewModel

你的UI控制器(即Activity或Fragment)需要了解您的ViewModel。您的UI控制器就可以在发生UI交互时显示数据并更新数据

但是,ViewModels不应保留对Activity,Fragment或Context的引用。 此外,ViewModels不应包含那些拥有对UI控制器的引用的元素,例如Views,因为这将创建对Context的间接引用。

你不该保存这些对象的原因是, ViewModels的寿命超出了特定的UI控制实例之外——如果您将Activity旋转3次,则您刚刚创建了三个不同的Activity实例,但是只有一个ViewModel实例。

考虑到这一点,让我们来创建这个UI控制器/ViewModel关联。你想要为在UI控制器中的ViewModel创建一个成员变量。那么在onCreate中,你应该这样写:

ViewModelProviders.of(<Your UI controller>).get(<Your ViewModel>.class)

引用最新版本2.2.0版本ViewModel你会发现,ViewModelProviders该类被弃用了。

参考一下lifecycle更新文档
在这里插入图片描述
那么在onCreate中,你应该这样写:

 mViewModel = new ViewModelProvider(<Your UI controller>).get(<Your ViewModel>.class)

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   mViewModel = new ViewModelProvider(this).get(ScoreViewModel.class);
   // Other setup code below...
}

注意: “ViewModel中没有上下文的规则有一个例外:

有时您可能需要一个Application的Context(而不是Activity的Context)来与诸如系统服务之类的东西一起使用。

那么,您可以将应用程序上下文储存在ViewModel中,因为应用程序上下文与应用程序生命周期相关联。

这不同于与Activity生命周期相关的Activity的Context。实际上如果需要Application的Context,则应该拓展AndroidViewModel,它只是一个包含Application引用的ViewModel。

/**
 * Application context aware {@link ViewModel}.
 * <p>
 * Subclasses must have a constructor which accepts {@link Application} as the only parameter.
 * <p>
 */
public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    @NonNull
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

3. 在UI控制器中使用ViewModel

要访问或更改UI数据,现在可以在ViewModel中使用数据。这里有一个新的onCreate方法和一个为队伍A增加分数的更新方法的例子:

@Override
protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_main);
   mViewModel = new ViewModelProvider(this).get(ScoreViewModel.class);
   
   displayForTeamA(mViewModel.scoreTeamA);
   displayForTeamB(mViewModel.scoreTeamB);
}

// An example of both reading and writing to the ViewModel
public void addOneForTeamA(View v) {
   mViewModel.scoreTeamA = mViewModel.scoreTeamA + 1;
   displayForTeamA(mViewModel.scoreTeamA);
}

ViewModel也可以很好地和其他架构组件一起工作。比如: LiveData,DataBinding等等。

4. 源码分析

Fragment分析流程

在Fragment中使用ViewModelProviders关联ui控制器和VM的时候一般都通过of方法去创建,具体来看一下of方法,至于2.2.0采用 new ViewModelProvider(this).get(ScoreViewModel.class)方法创建VM,最后都要分析ViewModelProvider.get 方法。

ViewModelProviders

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment) {
    return of(fragment, null);
}

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull Fragment fragment, @Nullable Factory factory) {
    Application application = checkApplication(checkActivity(fragment));
    if (factory == null) {
        factory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
    }
    return new ViewModelProvider(fragment.getViewModelStore(), factory);
}

of方法通过ViewModelProvider中的一个单例AndroidViewModelFactory创建了Factory帮助我们去创建VM,并且返回了一个ViewModelProvider。大家注意一下第一个参数fragment.getViewModelStore(),这个ViewModelStore是什么呢?接着看

ViewModelStore

public class ViewModelStore {

    private final HashMap<String, ViewModel> mMap = new HashMap<>();
		//存储VM
    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());
    }

    /**
     *  Clears internal storage and notifies ViewModels that they are no longer used.
     */
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

其实就是一个存储VM的容器,里面通过HashMap进行存和取。

下面我们看创建VM时的get()方法
在这里插入图片描述

ViewModelProvider

ViewModelProvider 本质是从传递进去的 ViewModelStore 来获取实例。如果没有传递,则利用 factory 去创建一个新的,并存储到 ViewModelStore。

public class ViewModelProvider {

    private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";
            
    private final Factory mFactory;
    private final ViewModelStore mViewModelStore;
    
    //1、        
    //ViewModelProvider 本质是从传递进去的 ViewModelStore 来获取实例。
    //如果没有传递,则利用 factory 去创建一个新的,并存储到 ViewModelStore。
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
    
    //5、ViewModelStore()的由来,通过owner.getViewModelStore()?
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
        this(owner.getViewModelStore(), factory);
    }
    
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
        mFactory = factory;
        mViewModelStore = store;
    }
    
    @NonNull
    @MainThread
    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");
        }
        //2、根据传递的modelClass 构建一个默认的Key
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    
    @NonNull
    @MainThread
    //3、获取viewmodel实例时,也可以自行指定Key
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
        //4、在ViewModelStore中拿到VM实例
        //ViewModelStore 一个真正用来存储ViewModel实例的集合。本质上是HashMap<String,ViewModel>
        ViewModel viewModel = mViewModelStore.get(key);

        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) (mFactory)).create(key, modelClass);
        } else {
            viewModel = (mFactory).create(modelClass);
        }
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }
}

get()方法就是获取VM实例的过程,如果VM不存在,则通过工厂去创建实例。具体工厂创建实现:

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    private static AndroidViewModelFactory sInstance;

    /**
     * Retrieve a singleton instance of AndroidViewModelFactory.
     *
     * @param application an application to pass in {@link AndroidViewModel}
     * @return A valid {@link AndroidViewModelFactory}
     */
    @NonNull
    public static AndroidViewModelFactory getInstance(@NonNull Application application) {
        if (sInstance == null) {
            sInstance = new AndroidViewModelFactory(application);
        }
        return sInstance;
    }

    private Application mApplication;

    /**
     * Creates a {@code AndroidViewModelFactory}
     *
     * @param application an application to pass in {@link AndroidViewModel}
     */
    public AndroidViewModelFactory(@NonNull Application application) {
        mApplication = application;
    }

    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        if (AndroidViewModel.class.isAssignableFrom(modelClass)) {
            //noinspection TryWithIdenticalCatches
            try {
                return modelClass.getConstructor(Application.class).newInstance(mApplication);
            } 
          .......
        }
        return super.create(modelClass);
    }
}

这个工厂就很简单了,就是通过反射来创建VM实例。

如果不是AndroidViewModel的子类,会调用父类的create方法,即NewInstanceFactory.create

/**
 * Simple factory, which calls empty constructor on the give class.
 */
public static class NewInstanceFactory implements Factory {

    private static NewInstanceFactory sInstance;

    /**
     * Retrieve a singleton instance of NewInstanceFactory.
     *
     * @return A valid {@link NewInstanceFactory}
     */
    @NonNull
    static NewInstanceFactory getInstance() {
        if (sInstance == null) {
            sInstance = new NewInstanceFactory();
        }
        return sInstance;
    }

    @SuppressWarnings("ClassNewInstance")
    @NonNull
    @Override
    public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
        //noinspection TryWithIdenticalCatches
        try {
            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);
        }
    }
}

到这里VM的创建过程就差不多了,而我们发现他并没有和生命周期有什么相关的东西,或者说VM是怎样保证的的数据不被销毁的呢?看了网上的一些文章,很多都说在of方法的时候传入了Fragment,并且通过HolderFragment持有ViewModelStore对象等等……比如:
在这里插入图片描述
但是在我这里发现跟他们的都不一样,我搜了一下ViewModelStores,发现它已经‘退役’了,应该是更新了版本。
在这里插入图片描述
并且它的注释也告诉了我们它的继承者:
在这里插入图片描述
也就是我们在of()方法中的:
在这里插入图片描述
在这里插入图片描述
也就是说谷歌已经将ViewModelStore集成到了Fragment中,一起去看一下吧:

Fragment.java

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

FragmentManagerImpl.java

private FragmentManagerViewModel mNonConfig;

......

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

我们可以看到代码到了我们熟悉的FragmentManagerImpl,它的里面持有了一个FragmentManagerViewModel实例,进去看看这个是个什么东西:

FragmentManagerViewModel

FragmentManagerViewModel.java

class FragmentManagerViewModel extends ViewModel {

    //创建FragmentVM的工厂
    private static final ViewModelProvider.Factory FACTORY = new ViewModelProvider.Factory() {
        @NonNull
        @Override
        @SuppressWarnings("unchecked")
        public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
            FragmentManagerViewModel viewModel = new FragmentManagerViewModel(true);
            return (T) viewModel;
        }
    };

    //从viewModelProvider中获取FragmentVM实例
    @NonNull
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }
    //非中断的Fragment集合
    private final HashSet<Fragment> mRetainedFragments = new HashSet<>();
    private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
    private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();

    private final boolean mStateAutomaticallySaved;
    // Only used when mStateAutomaticallySaved is true
    private boolean mHasBeenCleared = false;
    // Only used when mStateAutomaticallySaved is false
    private boolean mHasSavedSnapshot = false;

 
    FragmentManagerViewModel(boolean stateAutomaticallySaved) {
        mStateAutomaticallySaved = stateAutomaticallySaved;
    }

    
    //添加非中断Fragment
    boolean addRetainedFragment(@NonNull Fragment fragment) {
        return mRetainedFragments.add(fragment);
    }

    @NonNull
    Collection<Fragment> getRetainedFragments() {
        return mRetainedFragments;
    }
    //是否改销毁
    boolean shouldDestroy(@NonNull Fragment fragment) {
        if (!mRetainedFragments.contains(fragment)) {
            // Always destroy non-retained Fragments
            return true;
        }
        if (mStateAutomaticallySaved) {
            // If we automatically save our state, then only
            // destroy a retained Fragment when we've been cleared
            return mHasBeenCleared;
        } else {
            // Else, only destroy retained Fragments if they've
            // been reaped before the state has been saved
            return !mHasSavedSnapshot;
        }
    }

    boolean removeRetainedFragment(@NonNull Fragment fragment) {
        return mRetainedFragments.remove(fragment);
    }

		//获取VMStore
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
            viewModelStore = new ViewModelStore();
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }
    //销毁并清空VM
    void clearNonConfigState(@NonNull Fragment f) {
        if (FragmentManagerImpl.DEBUG) {
            Log.d(FragmentManagerImpl.TAG, "Clearing non-config state for " + f);
        }
        // Clear and remove the Fragment's child non config state
        FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
        if (childNonConfig != null) {
            childNonConfig.onCleared();
            mChildNonConfigs.remove(f.mWho);
        }
        // Clear and remove the Fragment's ViewModelStore
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore != null) {
            viewModelStore.clear();
            mViewModelStores.remove(f.mWho);
        }
    }

看代码我们发现它继承了VM,并且里面保存了VMStore,也就是说保存了VM,同时清空的操作也在这里面:clearNonConfigState()

clearNonConfigState

ViewModelStore.java

/**
 *  Clears internal storage and notifies ViewModels that they are no longer used.
 */
public final void clear() {
    for (ViewModel vm : mMap.values()) {
        vm.clear();
    }
    mMap.clear();
}

那么到底是什么时候来清空VM的呢?也就是说clear()是什么时候调用的呢?查看源码我们发现有两处:
在这里插入图片描述

  1. 也就是上面提到的FragmentViewModel.java里面的clearNonConfigState()方法,而这个方法只在FragmentManagerImpl.moveToState() 被调用了:
    FragmentManagerImpl.java

    if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f);
    boolean beingRemoved = f.mRemoving && !f.isInBackStack();
    if (beingRemoved || mNonConfig.shouldDestroy(f)) {
        boolean shouldClear;
        if (mHost instanceof ViewModelStoreOwner) {
            shouldClear = mNonConfig.isCleared();
        } else if (mHost.getContext() instanceof Activity) {
            Activity activity = (Activity) mHost.getContext();
            shouldClear = !activity.isChangingConfigurations();
        } else {
            shouldClear = true;
        }
      	//Fragment正在被移除或者应该清空的状态下
        if (beingRemoved || shouldClear) {
            mNonConfig.clearNonConfigState(f);
        }
        f.performDestroy();
        dispatchOnFragmentDestroyed(f, false);
    } else {
        f.mState = Fragment.INITIALIZING;
    }
    

    这个方法是在FragmentManagerImpl.java中的moveToState方法里面的,这个方法是跟随着Fragment的生命周期的,当这个方法被调用时,判断两个状态beingRemovedshoudClear然后调用clear()方法。关于moveToState()可以查看这篇文章:Fragment之底层关键操作函数moveToState

  2. 第二个调用的地方就是ComponentActivity.java

    getLifecycle().addObserver(new GenericLifecycleObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    if (!isChangingConfigurations()) {//不是改变状态配置的
                        getViewModelStore().clear();
                    }
                }
            }
    });
    

    关于ComponentActivity,如果有看过之前我分析过的关于Lifecycles的应该对它有所了解。FragmentActivity继承了它,而ComponentActivity里面通过Lifecycles观察生命周期,当接受到ON_DESTROY的事件时,清空VM。

Activity分析流程

ViewModelProviders

@NonNull
@MainThread
public static ViewModelProvider of(@NonNull FragmentActivity activity) {
    return of(activity, null);
}

@NonNull
@MainThread
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(activity.getViewModelStore(), factory);
}

ViewModelProvider内的流程与Fragment基本一致,主要是看activity.getViewModelStore() 是怎么获取的ViewModelStore
在这里插入图片描述

onRetainNonConfigurationInstance概念

再来看ComponentActivity的代码之前首先要了解一个概念。

Android横竖屏切换时会触发onSaveInstanceState,而还原时会产生onRestoreInstanceState,但是Android的Activity类还有一个方法名为onRetainNonConfigurationInstance和getLastNonConfigurationInstance这两个方法

当Device configuration发生改变时,将伴随Destroying被系统调用。通过这个方法可以像onSaveInstanceState()的方法一样保留变化前的Activity State,最大的不同在于onRetainNonConfigurationInstance这个方法可以返回一个包含有状态信息的Object,其中甚至可以包含Activity Instance本身。新创建的Activity可以继承大量来至于Parent Activity State信息。

用这个方法保存Activity State后,通过getLastNonConfigurationInstance()在新的Activity Instance中恢复原有状态。比如:

@Override
public Object onRetainNonConfigurationInstance() {
    final MyDataObject data = MyLoadedData();
    return data;
}

在恢复窗口时,我们可以不使用onRestoreInstanceState,而代替的是 getLastNonConfigurationInstance 方法。我们可以直接在onCreate中使用,比如

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
 
    final MyDataObject data = (MyDataObject) getLastNonConfigurationInstance();
    if (data == null) {
        data = loadMyData();
    }
    ...
}

这个方法最大的好处是:

  • 当Activity曾经通过某个资源得到一些图片或者信息,那么当再次恢复后,无需重新通过原始资源地址获取,可以快速的加载整个Activity状态信息。
  • 当Activity包含有许多线程时,在变化后依然可以持有原有线程,无需通过重新创建进程恢复原有状态。
  • 当Activity包含某些Connection Instance时,同样可以在整个变化过程中保持连接状态。

下边是需要特别注意的几点:

  • onRetainNonConfigurationInstance()在onSaveInstanceState()之后被调用。
  • 调用顺序同样介于onStop() 和 onDestroy()之间。

ComponentActivity

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {
        
    static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
    
    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) {
            //1、首先从`NonConfigurationInstances`来获取`ViewModelStore`实例对象
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            //2、如果不为空岂不是获取到的是同一个 ViewModel 实例对象,
            //以至于页面恢复重建后还能接着复用。
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                //3、`ViewModelStore`何时被存储到`NonConfigurationInstances`里面的?
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
        return mViewModelStore;
    }
        
}

onRetainNonConfigurationInstance 源码

因系统原因页面被回收时,会触发onRetainNonConfigurationInstance方法,所以 viewModelStore 对象此时会被存储在NonConfigurationInstance 中。在页面恢复重建时,会再次把这个 NonConfigurationInstance 对象传递到新的Activity 中实现对象复用。

public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner,
        ViewModelStoreOwner,
        HasDefaultViewModelProviderFactory,
        SavedStateRegistryOwner,
        OnBackPressedDispatcherOwner {
    /**
     * Retain all appropriate non-config state.  You can NOT
     * override this yourself!  Use a {@link androidx.lifecycle.ViewModel} if you want to
     * retain your own non config state.
     */
    @Override
    @Nullable
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();

        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last
            //3、如果NonConfigurationInstance保存了viewModelStore,把它取出来NonConfigurationInstance
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }

        if (viewModelStore == null && custom == null) {
            return null;
        }

        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        //1、把viewModelStore放到NonConfigurationInstances中并返回
        nci.viewModelStore = viewModelStore;
        //2、当页面被销毁时ViewModelStore就被保存起来了。
        return nci;
    }
}

到这里就能理解为什么页面配置更改数据不丢失了。

5. 总结

分析完源码之后,我们来总结一下整个流程:

  1. 通过ViewModelProviders.of(this).get(DemoViewModel::class.java)创建VM
  2. of()方法中传入 fragment.getViewModelStore()并且返回VMProvider
  3. 查看FragmentManagerImpl中getViewModelStore(),持有FragmentManagerViewModel对象
  4. 在FragmentManagerViewModel中清空VM操作 clearNonConfigState(),同时在ViewModelStore中clear()了ViewModel的value值
  5. 最后我们发现只有在ComponentActivity中观察到接收到ON_DESTROY的事件时同时并不是由于configuration发生变化时才会执行clear()操作;另外一处是在moveToState()方法中,满足beingRemoved和shouldClear状态也会清空VM

ViewModel 的使用注意事项:

  • 不要持有 Activity :ViewModel 不会因为 Activity 配置改变而被销毁,所以绝对不要持有那些跟 Activity 相关的类,比如Activity 里的某个 View,让 ViewModel 持有 Activity 会导致内存泄露,还要注意的是连 Lifecycle 也不行;
  • 不能访问 UI :ViewModel 应该只负责管理数据,不能去访问 UI,更不能持有它;

好了 整个流程就是这样了,并没有特别深入的去分析,但是基本的原理我们已经清楚了,Demo中也只是简单的使用了VM。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值