Jetpack ViewModel 使用及原理解析

Jetpack ViewModel 使用及原理解析,第1张

深入学习 Jetpack 系列的 Android Architecture Components 中的一些列组件,记录一下学习过程,本文是 ViewModel 的使用及原理解析,通过一个实际的例子,来体验 ViewModel 能给我们带来哪些不一样的功能?最后通过阅读 ViewModel 的源码,由浅入深一步一步探索其原理!

ViewModel 简介

ViewModel 是以能感知生命周期的方式存储和管理界面相关数据的组件。感知生命周期是说当 Activity 或 Fragment 创建第一个实例的时候,它开始创建;当 Activity 销毁或者 Fragment 分离的时候,它自动清理。存储和管理界面相关数据即 ViewModel 保存的数据,在页面因配置变更(如横竖屏切换、分辨率调整、权限变更、系统字体样式变更等)导致页面销毁重建之后依然也是存在的。

Activity 和 ViewModel 生命周期的对照图

1.ViewModel 的使用 1.1 自定义 ViewModel
/**
 * 继承自 ViewModel
 */
class TestViewModel : ViewModel() {
    val testLiveData: MutableLiveData<String> = MutableLiveData<String>()

    fun getTestString() {
        testLiveData.value = "测试ViewModel"
    }
}

/**
 * 继承自 AndroidViewModel
 * 它的构造方法接收一个 Application 类型的参数,方便在 ViewModel 中使用 Context
 */
class TestAndroidViewModel(application: Application) : AndroidViewModel(application) {
}

自定义 ViewModel,可以继承自 ViewModel 来实现,ViewModel 还存在一个子类:AndroidViewModel,也可以继承它来实现,它的构造方法接收一个 Application 类型的参数,方便在 ViewModel 中使用Context。

问题:它们之间的区别是什么?

ViewModel 本身没有办法获得 Context,AndroidViewModel 提供 Application 用作 Context ,并专门提供 Application 单例,所以通常可以做一些全生命周期的工作。

1.2 Activity 中使用
class TestViewModelActivity : BaseActivity() {
    private lateinit var viewModel: TestViewModel
    private lateinit var androidViewModel: TestAndroidViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)
        // 通过 ViewModelProviders 获取 ViewModel 实例
        viewModel = ViewModelProvider(this).get(TestViewModel::class.java)

        androidViewModel =
            ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory(application))
                .get(TestAndroidViewModel::class.java)

        viewModel.testLiveData.observe(this) {
            // TextView 设置获取到的值
            tvContent.text = it
        }

        btnUpdate.setOnClickListener {
            // 点击按钮后 ViewModel 获取新值
            viewModel.getTestString()
        }
    }
}

从使用上来看,继承自 AndroidViewModel 时获取 ViewModel 实例时需要传入参数:ViewModelProvider.Factory, 那么这个 Factory 的作用是什么呢?

当我们调用 ViewModelProvider(this).get(XXXViewModel::class.java) 方法获取 ViewModel 实例的时候,它内部会默认调用 ViewModel 无参构造方法来创建实例,但是当 ViewModel 的构造方法有参数依赖时,这时候就需要我们自定义 Factory 来实例化 ViewModel。

1.3 小结

简单使用,点击按钮后,经由 ViewModel 获取新的值,TextView 展示新获取到的值。在 onCreate() 方法中通过 ViewModelProvider 实例的 get() 方法获取 ViewModel 实例,然后获取 ViewModel 中声明的 LiveData,并添加观察者 Observer 实现关联订阅。LiveData 使用及原理可以参考Jetpack LiveData 使用及原理解析。

首先抛出三个个问题:

  1. ViewModel 的实例缓存在哪里?
  2. 为什么 Activity 旋转屏幕后 ViewModel 可以保存并恢复数据的?什么时候 ViewModel#onCleared 会被调用?
  3. 怎么实现的 Activity 与 Fragment、Fragment 与 Fragment 之间数据共享的呢?
2.ViewModel 原理解析

带着问题来解析,从源码中剖析出答案!由这一行代码作为切入点,分析 ViewModel 是谁创建的实例?

    // 通过 ViewModelProviders 获取 ViewModel 实例
    viewModel = ViewModelProvider(this).get(TestViewModel::class.java)
2.1 ViewModel 类
public abstract class ViewModel {
    @Nullable
    private final Map<String, Object> mBagOfTags = new HashMap<>();
    private volatile boolean mCleared = false;

    /**
     * It is useful when ViewModel observes some data and you need to clear this subscription to
     * prevent a leak of this ViewModel.
     */
     // ViewModel 不再被使用,将要销毁时调用该方法
    @SuppressWarnings("WeakerAccess")
    protected void onCleared() {
    }

    @MainThread
    final void clear() {
        mCleared = true;
        if (mBagOfTags != null) {
            synchronized (mBagOfTags) {
                for (Object value : mBagOfTags.values()) {
                    // see comment for the similar call in setTagIfAbsent
                    closeWithRuntimeException(value);
                }
            }
        }
        onCleared();
    }
    ......
}

ViewModel 是一个抽象类,比较简单,除了一个 onCleared() 方法,提供了释放 ViewModel 中资源的一个机会,在 ViewModel 不再被使用时,在 Activity、Fragment 的 onDestroy 生命周期中被调用。我们可以通过重写 onCleared()方法来处理一些额外的 *** 作,如取消 *** 作等。

2.2 ViewModelProvider 类
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    	// 分析1 -- owner.getViewModelStore()
        this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
                ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
                : NewInstanceFactory.getInstance());
    }
    
    // 构建 ViewModelProvider 使用提供的 Factory 来创建 ViewModel
    public ViewModelProvider(@NonNull ViewModelStoreOwner owner, @NonNull Factory factory) {
    	// ViewModelStoreOwner 中取出 ViewModelStore
        this(owner.getViewModelStore(), factory);
    }

    // 构建 ViewModelProvider 使用提供的 Factory 来创建 ViewModel
    public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    	// 反射创建 ViewModel 的工厂
        mFactory = factory;
        // 保存 ViewModelStore,用于存储 ViewModel
        mViewModelStore = store;
    }

通过代码解析,ViewModelProvider 类的作用是创建、及获取 ViewModel 实例的。

下面看其 ViewModelProvider ## get() 方法是如何获取 ViewModel 的。

2.3 ViewModelProvider ## get(@NonNull String key, @NonNull Class modelClass)
    private static final String DEFAULT_KEY =
            "androidx.lifecycle.ViewModelProvider.DefaultKey";
    
    @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");
        }
        // 默认 key 值加上传入的类名在 ViewModelStore 中取 ViewModel
        return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
    }
    
    @NonNull
    @MainThread
    public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    	// 通过 key 在 ViewModelStore 中取 Value(ViewModel)
        // ViewModelStore 是真正存储 ViewModel 的地方,其内部维护了一个 HashMap
        ViewModel viewModel = mViewModelStore.get(key);
		// 检查给定的 Object 是不是此类的实例,是:true 不是:false。
        if (modelClass.isInstance(viewModel)) {
            if (mFactory instanceof OnRequeryFactory) {
            	// 查询是否需要重新 attachToLifecycle 以及 OnRecreation 类是否需要执行
                ((OnRequeryFactory) mFactory).onRequery(viewModel);
            }
            // 查询到 直接返回
            return (T) viewModel;
        } else {
            //noinspection StatementWithEmptyBody
            if (viewModel != null) {
                // TODO: log a warning.
            }
        }
        // 查询不到 创建 ViewModel 实例,使用 Factory 反射创建
        if (mFactory instanceof KeyedFactory) {
            viewModel = ((KeyedFactory) mFactory).create(key, modelClass);
        } else {
            viewModel = mFactory.create(modelClass);
        }
        // 添加到 ViewModelStore 的 Map 中,然后返回 ViewModel
        mViewModelStore.put(key, viewModel);
        return (T) viewModel;
    }

源码注释很多,不再重复,这里也回答了,ViewModel 实例缓存到了哪里,缓存到 ViewModelStore 的 HashMap 中。

下面是灵魂画师,手绘的一张获取 ViewModel 的流程图,勿笑哈!

2.4 ViewModelStore 类
public class ViewModelStore {
    private final HashMap<String, ViewModel> mMap = new HashMap<>();
	// 内部使用 HashMap 来存储 ViewModel
    final void put(String key, ViewModel viewModel) {
        ViewModel oldViewModel = mMap.put(key, viewModel);
        if (oldViewModel != null) {
        	// 如果是新的 ViewModel 覆盖旧的,要及时清理并释放掉旧的
            oldViewModel.onCleared();
        }
    }

    final ViewModel get(String key) {
        return mMap.get(key);
    }

    Set<String> keys() {
        return new HashSet<>(mMap.keySet());
    }
	// 清理 ViewModelStore 内缓存的 ViewModel
    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.clear();
        }
        mMap.clear();
    }
}

ViewModelStore 类代码很短,内部使用 HashMap 来存储 ViewModel。其 key 为 DEFAULT_KEY + ViewModel 的 Class 对象底层类规范名称,其 value 为对应 ViewModel 对象。

2.5 ViewModelStoreOwner 类
@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
    /**
     * Returns owned {@link ViewModelStore}
     * @return a {@code ViewModelStore}
     */
    @NonNull
    ViewModelStore getViewModelStore();
}

ViewModelStoreOwner 仅仅是一个接口,注释的意思:它是 ViewModelStore 的拥有者,内部有一个方法获取 ViewModelStore 实例。因为 Activity、Fragment 实现了这个接口,即 Activity、Fragment 是 ViewModelStore 的拥有者。

分析1 – owner.getViewModelStore() 方法获取 ViewModelStore,这里的 owner 就是 ComponentActivity。下面跟踪一下 Activity 的子类 ComponentActivity 的源码,一探究竟。

2.6 ComponentActivity ## getViewModelStore() 方法
public class ComponentActivity extends androidx.core.app.ComponentActivity implements
        LifecycleOwner, ViewModelStoreOwner, HasDefaultViewModelProviderFactory, ... XXX {
	// 这个类很重要,后面还会提到,和 Activity 类中的要区分开
	static final class NonConfigurationInstances {
        Object custom;
        ViewModelStore viewModelStore;
    }
    private ViewModelStore mViewModelStore;
    private ViewModelProvider.Factory mDefaultFactory;

	public ComponentActivity() {
        ......
        // Activity 在创建的时候,会给 Lifecycle 订阅一个 LifecycleEventObserver 观察者
		// 来感知 Lifecycle 发过来的销毁事件信号,然后执行 ViewModelStore 清理方法。
		// 这里解释了什么时候 ViewModel#onCleared 会被调用
        getLifecycle().addObserver(new LifecycleEventObserver() {
            @Override
            public void onStateChanged(@NonNull LifecycleOwner source,
                    @NonNull Lifecycle.Event event) {
                if (event == Lifecycle.Event.ON_DESTROY) {
                    // Clear out the available context
                    mContextAwareHelper.clearAvailableContext();
                    // And clear the ViewModelStore
                    // isChangingConfigurations() 返回 true 则不清理,即配置改变导致的则不清理
                    if (!isChangingConfigurations()) {
                        getViewModelStore().clear();
                    }
                }
            }
        });
    }

    @NonNull
    @Override
    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.");
        }
        ensureViewModelStore();
        return mViewModelStore;
    }
	// 获取 ViewModelStore -- 恢复或新建
	void ensureViewModelStore() {
        if (mViewModelStore == null) {
        	// 初始是为空的,从 Activity##getLastNonConfigurationInstance() 获取
        	// mLastNonConfigurationInstances 是在 AMS 通知启动 Activity 时
        	// 在 ActivityThread ## performLaunchActivity()方法中调用 activity.attach() 方法中赋值的
        	// 方法中有一个参数 ActivityClientRecord.lastNonConfigurationInstances
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                // 从 NonConfigurationInstances 恢复 ViewModelStore 
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
            	// 获取不到,则新建一个
                mViewModelStore = new ViewModelStore();
            }
        }
    }
}

这里解释了什么时候 ViewModel#onCleared 会被调用。 Activity 在创建的时候,会给 Lifecycle 订阅一个 LifecycleEventObserver 观察者来感知 Lifecycle 发过来的销毁事件信号,然后执行 ViewModelStore 清理方法。ViewModelStore 内部会遍历 HashMap 并调用 ViewModel # clear() 方法,最后 ViewModel # onCleared 会被调用,这里有一个判断,即如果是因为配置改变导致的销毁则不清理。
Lifecycle 使用及原理可以参考Jetpack Lifecycle 使用及原理解析。

2.7 Activity ## getLastNonConfigurationInstance() 方法
	// Activity 类中的静态内部类
    static final class NonConfigurationInstances {
        Object activity;
        HashMap<String, Object> children;
        FragmentManagerNonConfig fragments;
        ArrayMap<String, LoaderManager> loaders;
        VoiceInteractor voiceInteractor;
    }
    
    @UnsupportedAppUsage
    /* package */ NonConfigurationInstances mLastNonConfigurationInstances;
    
    @Nullable
    public Object getLastNonConfigurationInstance() {
    	// 不为空,则返回存储在 NonConfigurationInstances 中的对象 activity
    	// 这个 activity 实际上就是 ComponentActivity 的内部类 NonConfigurationInstances 下面有解析
    	// 分析2 -- 那这个 activity 是什么时候赋值的呢?
        return mLastNonConfigurationInstances != null
                ? mLastNonConfigurationInstances.activity : null;
    }

    @UnsupportedAppUsage
    final void attach(Context context, ActivityThread aThread,
            ......
            NonConfigurationInstances lastNonConfigurationInstances,
            ......) {
		......
		// mLastNonConfigurationInstances 赋值为 ActivityClientRecord.lastNonConfigurationInstances
		// 分析3 -- ActivityClientRecord.lastNonConfigurationInstances 又是由谁来赋值呢?
        mLastNonConfigurationInstances = lastNonConfigurationInstances;
        ......
    }

ActivityClientRecord.lastNonConfigurationInstances 又是由谁在哪里赋值呢?

我们知道,在屏幕旋转后页面的配置会改变,会走销毁重建这一过程,生命周期如下:onStop -> onSaveInstanceState -> onRetainNonConfigurationInstance -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume。这里我们先去看看 onDestroy 吧,因为如果我们想保存旋转之前的状态 lastNonConfigurationInstances,并在旋转后恢复之前保存的状态。至少也要在 onDestroy 之前保存,大胆猜测一下 onDestroy 之前存储了页面有关的信息,现在就需要看 onDestroy 是哪里触发的? 看能不能回答上面的问题。

2.8 ActivityThread ## performDestroyActivity() 方法
ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
            int configChanges, boolean getNonConfigInstance, String reason) {
        ActivityClientRecord r = mActivities.get(token);
        ...... 精简了一些代码
        if (r != null) {
            performPauseActivityIfNeeded(r, "destroy");
            if (!r.stopped) {
                callActivityOnStop(r, false /* saveState */, "destroy");
            }
            // 判断是否有 LastNonConfigInstance 实例
            if (getNonConfigInstance) {
                try {
                	// ActivityClientRecord.lastNonConfigurationInstances 是在这里赋值的
                	// 在 onStop 之后,onDestroy 之前,这也回答了 分析3 
                    r.lastNonConfigurationInstances = r.activity.retainNonConfigurationInstances();
                }
                ......
            }
            try {
                r.activity.mCalled = false;
                // 销毁当前 Activity
                mInstrumentation.callActivityOnDestroy(r.activity);
                ......
            }
            ...... 精简了一些代码
        }
        return r;
    }

分析3 – 熟悉 Activity 启动流程的都应该知道,ActivityThread 中有一些列跟 Activity 什么周期有关的 performXXXActivity() 方法,这里我们去看一下 performDestroyActivity() 方法,果然在这里找到了我们想要的东西,在 onStop 之后,onDestroy 之前,会触发页面状态的保存,ActivityClientRecord.lastNonConfigurationInstances 被赋值,继续一步步探究源码。

2.9 Activity ## retainNonConfigurationInstances() 方法
    NonConfigurationInstances retainNonConfigurationInstances() {
    	// 分析4 -- 这里获取到 activity
        Object activity = onRetainNonConfigurationInstance();
        HashMap<String, Object> children = onRetainNonConfigurationChildInstances();
        FragmentManagerNonConfig fragments = mFragments.retainNestedNonConfig();

        // We're already stopped but we've been asked to retain.
        // Our fragments are taken care of but we need to mark the loaders for retention.
        // In order to do this correctly we need to restart the loaders first before
        // handing them off to the next activity.
        mFragments.doLoaderStart();
        mFragments.doLoaderStop(true);
        ArrayMap<String, LoaderManager> loaders = mFragments.retainLoaderNonConfig();

        if (activity == null && children == null && fragments == null && loaders == null
                && mVoiceInteractor == null) {
            return null;
        }
		// 新建 NonConfigurationInstances 实例,保存页面状态值
        NonConfigurationInstances nci = new NonConfigurationInstances();
        // 将上面获取到的 activity 存储到 NonConfigurationInstances 实例中
        nci.activity = activity;
        nci.children = children;
        nci.fragments = fragments;
        nci.loaders = loaders;
        if (mVoiceInteractor != null) {
            mVoiceInteractor.retainInstance();
            nci.voiceInteractor = mVoiceInteractor;
        }
        return nci;
    }

分析2 – 看方法名,保留 NonConfigurationInstances,方法内部首先通过 onRetainNonConfigurationInstance() 方法获取到 activity 对象,注意这个 activity 不是我们的页面,就是一个对象,别被误导。最后将上面获取到的 activity 存储到 NonConfigurationInstances 实例中。

Activity 中的 onRetainNonConfigurationInstance() 方法返回为空,所以要看它的实现类 ComponentActivity 中的重写的方法。

2.10 ComponentActivity ## onRetainNonConfigurationInstance() 方法
	// ComponentActivity 类中的静态内部类
	static final class NonConfigurationInstances {
    	Object custom;
    	ViewModelStore viewModelStore;
	}
	
    @Override
    @Nullable
    @SuppressWarnings("deprecation")
    public final Object onRetainNonConfigurationInstance() {
        // Maintain backward compatibility.
        Object custom = onRetainCustomNonConfigurationInstance();
		// 存储 mViewModelStore
        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            // No one called getViewModelStore(), so see if there was an existing
            // ViewModelStore from our last NonConfigurationInstance
            // 如果没有调用过 getViewModelStore() 方法,则从 last NonConfigurationInstance 中获取
            // 看是否有 ViewModelStore
            NonConfigurationInstances nc =
                    (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }
		// 如果仍然没有找到、则返回空
        if (viewModelStore == null && custom == null) {
            return null;
        }
		// 将 ViewModelStore 存储到 NonConfigurationInstances 中,并返回
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }

分析4 – 方法中将 ViewModelStore 存储到 ComponentActivity 类中的静态内部类 NonConfigurationInstances 中的 viewModelStore 中,并返回给 Activity 类中的静态内部类 NonConfigurationInstances 中的 activity 中存储。

注意:区分上面说到的两个 NonConfigurationInstances,不要将他们搞混了,有助于理解源码。

综上分析,ViewModelStore 在页面因配置改变而导致销毁重建时,通过 onRetainNonConfigurationInstance() 的回调被保存在了 NonConfigurationInstances 实例中,该实例被 ActivityThread 中的 ActivityClientRecord 持有。下次 Activity 重建时,由 ActivityThread.performLaunchActivity 方法中调用 Activity.attach 方法,再将 NonConfigurationInstances 实例传给重建后的 Activity 的 mLastNonConfigurationInstances。

2.11 小结

我们知道,在屏幕旋转后页面的配置会改变,会走销毁重建这一过程,生命周期如下:onStop -> onSaveInstanceState -> onRetainNonConfigurationInstance -> onDestroy -> onCreate -> onStart -> onRestoreInstanceState -> onResume。

所以可以在 onSaveInstanceState() 和 onRetainNonConfigurationInstance() 方法来保存当前的状态,一般情况我们保存的数据不是太大时,适合放在 Bundle 中,这时 onSaveInstanceState() 方法比较合适,如果要保存的数据不适合放在 Bundle 中或是数据比较大,那么这时我们就应该使用 onRetainNonConfigurationInstance(),而且我们使用onRetainNonConfigurationInstance() 可以保存任何类型的对象,这些类型的数据可能会被一个重建的 Activity 重新使用。

也就是说 onSaveInstanceState() 中 Bundle 只能放一些特定的类型,比如基本数据类型、数组、Serialable 对象,而onRetainNonConfigurationInstance() 中只要是个 Object 对象就可以。

同时 onRetainNonConfigurationInstance() 更多的是在配置改变时 *** 作的,这个时候保存一些不会因为配置改变而发生改变的东西,这也符合 ViewModel 的设计初衷。而且 onSaveInstanceState() 数据是序列化保存到磁盘中,而 onRetainNonConfigurationInstance() 保存的数据是存在内存中,考虑到数据恢复时的效率,官⽅最终采⽤了 onRetainNonConfigurationInstance() 的⽅式来恢复 ViewModel。

3.Fragment 中 ViewModel 的创建和获取

为何这里要单独拿出来写呢?在 Fragment 中创建并获取 ViewModel 与在 Activity 中创建是不一样的,但是 Fragment 中的 ViewModel 与其宿主 FragmentActivity 有着密切的联系。要了解 ViewModel 与 Fragment 的创建、获取过程,我们先来了解一下 FragmentManager 与 FragmentManagerViewModel 相关知识。

3.1 FragmentManager

每个 Fragment 及宿主 Activity (继承⾃ FragmentActivity) 都会在创建时,初始化⼀个 FragmentManager 对象管理 Fragment。

  1. 对于宿主 Activity,getSupportFragmentManager() 获取的是 FragmentActivity 的 FragmentManager 对象。
  2. 对于 Fragment,getFragmentManager() 是获取的⽗ Fragment (如果没有,则是 FragmentActivity) 的 FragmentManager 对象,⽽ getChildFragmentManager() 是获取⾃⾝的 FragmentManager 对象。
3.2 FragmentManagerViewModel
final class FragmentManagerViewModel extends ViewModel {
    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;
        }
    };

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

    private final HashMap<String, Fragment> mRetainedFragments = new HashMap<>();
    // 存储 Fragment 的 FragmentManagerViewModel
    private final HashMap<String, FragmentManagerViewModel> mChildNonConfigs = new HashMap<>();
    // 存储 Fragment 的 ViewModelStore
    private final HashMap<String, ViewModelStore> mViewModelStores = new HashMap<>();
    ......
}

每个 Fragment 创建并绑定到宿主 Host 时,都会创建⼀个 FragmentManagerViewModel 对象,在该对象中主要存储其⼦ Fragment 的 ViewModelStore 与 FragmentManagerViewMoel。上述两个 Map 对应的 Key 值都为 Fragment 的唯⼀ UUID,该 UUID 会在 Fragment 对象创建时⾃动⽣成。也就是每个 Fragment 对应唯⼀ UUID。

3.3 宿主 FragmentActivity 中 FragmentManagerViewModel 的创建、获取 3.3.1 FragmentActivity 宿主

Fragment 是依附于 FragmentActivity 的,因此对 FragmentManager 的 *** 作一定也是通过 FragmentActivity 来完成的。宿主 FragmentActivity 创建时,默认会在其 FramgentManager 中创建⼀个 FragmentManagerViewModel,同时将⽣成的 FragmentManagerViewModel 存储在宿主 FragmentActivity 的 ViewModelStore 中。下面跟着代码看一下,在哪里创建的?

public class FragmentActivity extends ComponentActivity implements
        ActivityCompat.OnRequestPermissionsResultCallback, ... {
    // FragmentController 其实是一个代理类,代理 FragmentManager 那些方法
    final FragmentController mFragments = FragmentController.createController(new HostCallbacks());
    public FragmentActivity() {
        super();
        init();
    }
    
    private void init() {
		......
        addOnContextAvailableListener(new OnContextAvailableListener() {
            @Override
            public void onContextAvailable(@NonNull Context context) {
            	// mFragments 是 FragmentController,内部通过 FragmentHostCallback 间接控制 FragmentManager
            	// 注意:这里传入的是 null
                mFragments.attachHost(null /*parent*/);
                ......
            }
        });
    }
}

mFragments 是 FragmentController,内部通过 FragmentHostCallback 来间接控制 FragmentManager。 每个 Activity 都有自己的 FragmentController 通过代理 FragmentManager 来管理 Fragment。

3.3.2 FragmentController ## attachHost() 方法
    public void attachHost(@Nullable Fragment parent) {
    	// 调用宿主 FragmentActivity 的 FragmentManager 来 attachController
    	// FragmentHostCallback 绑定到 FragmentManager 管理 Fragment 
        mHost.mFragmentManager.attachController(
                mHost, mHost /*container*/, parent);
    }
3.3.3 FragmentManager ## attachController() 方法
    @SuppressLint("SyntheticAccessor")
    void attachController(@NonNull FragmentHostCallback<?> host,
            @NonNull FragmentContainer container, @Nullable final Fragment parent) {
        if (mHost != null) throw new IllegalStateException("Already attached");
        mHost = host;
        mContainer = container;
        mParent = parent;
        
        // 获取 FragmentManagerViewModel
        if (parent != null) {
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
        	// parent 为空,host 是 FragmentHostCallback 实现了 ViewModelStoreOwner 接口
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            // FragmentManagerViewModel 使用宿主 FragmentActivity 的 ViewModelStore 获取 FragmentManagerViewModel
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }

FragmentHostCallback 实现了 ViewModelStoreOwner 接口,调用其 getViewModelStore() 方法,最后调用到 FragmentActivity 的 getViewModelStore() 方法来获取 FragmentActivity 的 ViewModelStore。

3.3.4 FragmentManagerViewModel ## getInstance() 方法
    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;
        }
    };

    @NonNull
    static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
    	// 创建 ViewModelProvider 传入的是宿主 FragmentActivity 中的 ViewModelStore
        ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
                FACTORY);
        return viewModelProvider.get(FragmentManagerViewModel.class);
    }

该方法中,会创建 FragmentManagerViewModel 实例,并将其添加到宿主 FragmentActivity 中的 ViewModelStore 中。

流程大致如下图所示:

3.3.5 小结

宿主 FragmentActivity 中获取 FragmentManagerViewModel,host.getViewModelStore() 方法获取的是宿主 FragmentActivity 的 ViewModelStore,然后通过 FragmentManagerViewModel.getInstance(viewModelStore) 方法创建 FragmentManagerViewModel,通过分析我们发现,创建的实例也存储在宿主 FragmentActivity 的 ViewModelStore 中。

所以,FragmentActivity 的 ViewModelStore 中不仅有 Activity 环境下创建的 ViewModel,还包含了一个(或多个)根 Fragment 对应的 FragmentManagerViewModel。

3.4 根 Fragment 中 ViewModel 的创建、获取

在根 Fragment 中创建时,需要从宿主 FragmentActivity 或⽗ Fragment 中的 FramgentManager 中获取对应的 FragmentManagerViewModel,并使⽤⾃⾝的 ChildFragmentManager 中 mNonConfig 变量进⾏保存。

3.4.1 Fragment ## performAttach() 方法
    void performAttach() {
        for (OnPreAttachedListener listener: mOnPreAttachedListeners) {
            listener.onPreAttached();
        }
        mOnPreAttachedListeners.clear();
        // 调用 Fragment 自身的 FragmentManager 来 attachController,注意和 Activity 绑定时的区别
        // 这里传入的 this 代表是当前 Fragment 自身
        mChildFragmentManager.attachController(mHost, createFragmentContainer(), this);
        mState = ATTACHED;
        mCalled = false;
        onAttach(mHost.getContext());
        ......
        mFragmentManager.dispatchOnAttachFragment(this);
        mChildFragmentManager.dispatchAttach();
    }

此时,又会调用 FragmentManager 的 attachController() 方法,但是入参 parent 不一样,此时 parent 不为空。

3.4.2 FragmentManager ## attachController() 方法
    @SuppressLint("SyntheticAccessor")
    void attachController(@NonNull FragmentHostCallback<?> host,
            @NonNull FragmentContainer container, @Nullable final Fragment parent) {
        if (mHost != null) throw new IllegalStateException("Already attached");
        mHost = host;
        mContainer = container;
        mParent = parent;
        
        // 获取 FragmentManagerViewModel
        if (parent != null) {
        	// parent 不为空,根据具体场景 parent 指代的 FragmentManager 也不同
            mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
        } else if (host instanceof ViewModelStoreOwner) {
            ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
            mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
        } else {
            mNonConfig = new FragmentManagerViewModel(false);
        }
    }

注意:当 Fragment 是⼦ Fragment 时,parent.fragmentManager 的值为⽗ Fragment 的 FragmentManager,否则为 Activity 中的 FragmentManager。

3.4.3 FragmentManager ## getChildNonConfig() 方法
public abstract class FragmentManager implements FragmentResultOwner {
    private FragmentManagerViewModel mNonConfig;
    @NonNull
    private FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
    	// mNonConfig 是 FragmentManagerViewModel 的实例
        return mNonConfig.getChildNonConfig(f);
    }
}
3.4.4 FragmentManagerViewModel ## getChildNonConfig() 方法
    @NonNull
    FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
    	// 如果存在、直接返回
        FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
        if (childNonConfig == null) {
        	// 如果不存在、新建一个并存入 mChildNonConfigs
            childNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);
            mChildNonConfigs.put(f.mWho, childNonConfig);
        }
        return childNonConfig;
    }

在该⽅法中,会从 Activity 中的 FragmentManagerViewModel 中的 mChildNonConfigs 中获取 Fragment 的
FragmentManagerViewModel,如果有则直接返回。反之,新建一个并存⼊ mChildNonConfigs 中。

FragmentManagerViewModel 创建、获取的流程如下图所示:

3.4.5 Fragment ## getViewModelStore() 方法
public class Fragment implements ..., LifecycleOwner,
			ViewModelStoreOwner, HasDefaultViewModelProviderFactory {
	@NonNull
    @Override
    public ViewModelStore getViewModelStore() {
        if (mFragmentManager == null) {
            throw new IllegalStateException("Can't access ViewModels from detached fragment");
        }
        if (getMinimumMaxLifecycleState() == Lifecycle.State.INITIALIZED.ordinal()) {
            ...
        }
        return mFragmentManager.getViewModelStore(this);
    }	
}

Fragment 的 getViewModelStore 是从 FragmentManager.getViewModelStore 中获取的。

3.4.6 FragmentManager ## getViewModelStore() 方法
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
        return mNonConfig.getViewModelStore(f);
    }

mNonConfig 是 FragmentManagerViewModel 的实例。

3.4.7 FragmentManagerViewModel ## getViewModelStore() 方法
    @NonNull
    ViewModelStore getViewModelStore(@NonNull Fragment f) {
    	// 获取 ViewModelStore,找到则返回
        ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
        if (viewModelStore == null) {
        	// 获取不到,则新建并存储到 ViewModelStore
            viewModelStore = new ViewModelStore();
            mViewModelStores.put(f.mWho, viewModelStore);
        }
        return viewModelStore;
    }

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

ViewModel 创建、获取的流程如下图所示:

3.4.8 小结

在根 Fragment 中创建、获取时,需要从宿主 FragmentActivity 或⽗ Fragment 中的 FramgentManager 中获取对应的 FragmentManagerViewModel,并使⽤⾃⾝的 ChildFragmentManager 中 mNonConfig 变量进⾏保存。

查找在 mNonConfig 保存的 FragmentManagerViewModel 中的 mChildNonConfigs 这个 HashMap 中是否有子 Fragment 的 FragmentManagerViewModel,没有就创建,并缓存到 mChildNonConfigs 中。然后继续查找在 mNonConfig 保存的 FragmentManagerViewModel 中的 mViewModelStores 这个 HashMap 中是否有子 Fragment 的 ViewModelStore,没有就创建,并缓存到 mViewModelStores 中。最后获取 ViewModel 的过程就跟第2节的流程类似,从 ViewModelStore 中查找、获取,有就返回,没有则新建并存入 ViewModelStore 中。

4. 总结 4.1 ViewModel 的实例缓存在哪里?

通过上面的源码解析,ViewModel 的实例缓存在 ViewModelStore 的 HashMap 中。在页面销毁重建时,这个 ViewModelStore 又会保存在 ActivityClientRecord.lastNonConfigurationInstances,在页面重建时 ActivityThread.performLaunchActivity 方法中调用 Activity.attach 方法,再将 NonConfigurationInstances 实例传给重建后的 Activity 的 mLastNonConfigurationInstances,以此做到页面数据留存,具体可以细细研读上面的源码解析。

4.2 为什么 Activity 旋转屏幕后 ViewModel 可以保存并恢复数据的?什么时候 ViewModel#onCleared 会被调用?

我们知道,页面在旋转屏幕后会导致配置信息的改变,并触发页面的销毁重建,这时存储有 ViewModel 的 ViewModelStore 会保存在 ActivityClientRecord.lastNonConfigurationInstances,在页面重建时 ActivityThread.performLaunchActivity 方法中调用 Activity.attach 方法,再将 NonConfigurationInstances 实例传给重建后的 Activity 的 mLastNonConfigurationInstances,以此做到页面数据留存和恢复,具体可以细细研读上面的源码解析。

这里还需进一步解析源码,结合 AMS 来分析,为什么页面销毁的时候,ActivityThread 的 ActivityClientRecord 中保存的数据没有被清空,在下一篇文章中继续分析,分析 ViewModel 在配置改变后是怎么留存数据的。

Activity 在创建的时候,会给 Lifecycle 订阅一个 LifecycleEventObserver 观察者来感知 Lifecycle 发过来的销毁事件信号,然后执行 ViewModelStore 清理方法。ViewModelStore 内部会遍历 HashMap 并调用 ViewModel # clear() 方法,最后 ViewModel # onCleared 会被调用,注意,此时会做一个判断,即如果是因为配置改变导致的销毁则不清理。

4.3 怎么实现的 Activity 与 Fragment、Fragment 与 Fragment 之间数据共享的呢?
class TestViewModelActivity : BaseActivity() {
    private lateinit var viewModel: TestViewModel
    private lateinit var viewModelFragmentThis: TestViewModel
    private lateinit var viewModelFragmentRequire: TestViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_live_data)
        // Activity 获取 ViewModel 实例
        viewModel = ViewModelProvider(this).get(TestViewModel::class.java)
        // Fragment 获取 ViewModel 实例、这里只是为了对照比较哈
        viewModelFragmentThis = ViewModelProvider(this).get(TestViewModel::class.java)
        viewModelFragmentRequire = ViewModelProvider(requireActivity()).get(TestViewModel::class.java)
    }
}

由于 Fragment 同样也是实现了 ViewModelStoreOwner 接口,因此同在 Activity 中创建和获取 ViewModel 一样,给 ViewModelProvider 的构造函数传入自己的实例即可。

但如果 Fragment 要和其宿主 Activity 共用一个 ViewModel呢,可以向 ViewModelProvider 的构造函数传入其宿主 Activity 即可,这样 Activity 和 Fragment 之间就可以实现数据共享。

如果 Fragment 和 Fragment 之间要实现数据共享,即共用一个 ViewModel,则两个 Fragment 在获取 ViewModel 时可以向 ViewModelProvider 的构造函数传入它们共同的宿主 Activity 即可。

相信读完上面的源码解析过程,不难理解这个问题的答案哈!

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/923100.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-16
下一篇 2022-05-16

发表评论

登录后才能评论

评论列表(0条)

保存