Jetpack ViewModel

Jetpck 才是真的豪华全家桶

引言

  • 先看个视频介绍 : 【架构组件之 ViewModel 介绍】
  • ViewModel 主要用于存储 UI 数据以及生命周期感知的数据。
  • ViewModel 不受系统配置变更 Activity 销毁重建的影响。

预览图

Jetpack ViewModel 概览图

1. 诞生

ViewModel : 从界面控制中分离出视图数据所有权的操作更容易且更高效。

  • Activity 的 onSaveInstanceState() 方法从 onCreate() 捆绑包恢复其数据,仅限支持序列化的少量数据。
  • 界面控制对于异步调用,需要确保系统在其销毁后清理这些调用以避免潜在的内存泄漏。配置更改后重新创建对象,会造成资源浪费。
  • 界面控制如果负责从数据库或网络加载数据,那么会使类越发膨胀。过多的职责,会加大测试难度。

1.1 保留界面状态的选项

 保留界面状态的每个选项都有所差异:

保留界面状态的选项

  • 每种方式都有其独特的使用场景和价值,根据场景选择即可。
  • ViewModel 非常适合在用户正活跃地使用应用时存储和管理界面相关数据。它支持快速访问界面数据,并且有助于避免在发生旋转、窗口大小调整和其他常见的配置变更后从网络或磁盘中重新获取数据。
  • ViewModel 与已保存实例状态不同,在系统发起的进程终止过程中会被销毁。使用 onSaveInstanceState() 作为后备方法来处理系统发起的进程终止。

1.2 ViewModel SavedStateHandle

 上面提到,ViewModel 对象可以处理配置更改,因此您无需担心旋转时或其他情况下的状态。但是,如果您需要处理系统发起的进程终止,则可以使用 onSaveInstanceState()作为备用方式。

 这样会有一个问题,界面状态通常在 ViewModel 对象中(而不是 Activity 中)存储或引用。

 此时就需要 SavedStateHandle 来处理。

1.2.1 代码实例

//RestoreViewModel
//构造函数的参数 SavedStateHandle
class RestoreViewModel(savedStateHandle: SavedStateHandle) : ViewModel() {
    companion object {
        private const val USER = "user"
        private const val NAME = "name"
    }

    val name = MutableLiveData<String?>()

    init {
        //获取 savedStateHandle 中的 bundle,进行键值对获取
        val restoreBundle = savedStateHandle.get<Bundle>(USER)
        if (restoreBundle != null) {
            if (restoreBundle.containsKey(NAME)) {
                name.value = restoreBundle.getString(NAME)
                Log.d(TAG, "init() savedStateHandle restore name = ${name.value}")
            } else {
                name.value = null
            }
        }

        //退到后台,或者屏幕旋转等,保存状态
        savedStateHandle.setSavedStateProvider(USER) {
            if (name.value != null) {
                Log.d(TAG, "init() savedStateHandle saveState name = ${name.value}")
                bundleOf(NAME to name.value)
            } else {
                Bundle()
            }
        }
    }
}


//Activity
const val TAG = "ViewModelActivity"
class ViewModelActivity : AppCompatActivity() {

    var restoreModel: RestoreViewModel? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)

        restoreModel = ViewModelProvider(this).get(RestoreViewModel::class.java)

        restoreModel!!.name.observe(this) { name ->
            Log.d(TAG, "onCreate() restoreModel update name = $name ")
        }
        Log.d(TAG, "onCreate() restoreModel origin name = ${restoreModel!!.name.value} ")
    }
    
    fun onRenameClick(view: View) {
        var name = "kejiyuanren ".plus(Random.nextInt(100))
        Log.d(TAG, "onRenameClick() called with: name = $name")
        restoreModel!!.name.value = name
    }

    override fun onSaveInstanceState(outState: Bundle) {
        Log.d(TAG, "onSaveInstanceState() called with: outState = $outState")
        super.onSaveInstanceState(outState)
    }
}
复制代码

1.2.2 Log 输出

step1 : 应用启动
ViewModelActivity: onCreate() restoreModel origin name = null

step2 : name 命名 button 点击
ViewModelActivity: onRenameClick() called with: name = kejiyuanren 6
ViewModelActivity: onCreate() restoreModel update name = kejiyuanren 6

step3 : 按 home 键
ViewModelActivity: onSaveInstanceState() called with: outState = Bundle[{}]
ViewModelActivity: init() savedStateHandle saveState name = kejiyuanren 6

step4 : kill 应用,重新启动
ViewModelActivity: init() savedStateHandle restore name = kejiyuanren 6
ViewModelActivity: onCreate() restoreModel origin name = kejiyuanren 6 
ViewModelActivity: onCreate() restoreModel update name = kejiyuanren 6
复制代码

注意SavedStateRegistry 将数据存储在与 onSaveInstanceState() 相同的 Bundle 中,因此需要遵守相同的考虑因素和数据限制。

2. 使用

2.1 依赖添加

 官方指导文档

dependencies {
    def lifecycle_version = "2.3.1"
    def activity_version = "1.2.3"
    def fragment_version = "1.3.5"

    //ViewModel 依赖引入
    implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
    
    // 使用 'by viewModels()' 进行创建 ViewModel
    implementation("androidx.activity:activity-ktx:$activity_version")
    implementation("androidx.fragment:fragment-ktx:$fragment_version")
}
复制代码

2.2 生命周期

Activity vs ViewModel

ViewModel 生命周期

 通常在系统首次调用 Activity 对象的 onCreate() 方法时请求 ViewModel。系统可能会在 activity 的整个生命周期内多次调用 onCreate(),如在旋转设备屏幕时。ViewModel 存在的时间范围是从首次请求 ViewModel 直到 activity 完成并销毁。

2.3 实现 ViewModel

 架构组件为界面控制器提供了 ViewModel 辅助程序类,该类负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 activity 或 fragment 实例使用。

2.3.1 ViewModel 定义,组合了 LiveData

class UserViewModel : ViewModel() {
    val users = MutableLiveData<String>()
}
复制代码

2.3.2 Activity 创建 ViewModel,并模拟修改其中的 LiveData 属性值为 “kejiyuanren”

class ViewModelActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_view_model)

        //方式 1, 通过创建 ViewModelProvider 来创建 ViewModel
        val model: UserViewModel = ViewModelProvider(this).get(UserViewModel::class.java)

        //方式 2 ,by viewModels(),最终与方式1 实现策略一样,更简洁(需要引入 activity-ktx)
//        val model: UserViewModel by viewModels()
        
        model.users.observe(this) { name ->
            Log.d(TAG, "update ui user name = : $name")
        }
        model.users.value = "kejiyuanren"
    }
}
复制代码

2.3.3 Log 输出,LiveData 监听到了属性值变更,观察输出

ViewModelActivity: update ui user name = : kejiyuanren
复制代码

2.4 Fragment 共享数据

 Activity 中的两个或更多 Fragment 需要相互通信是一种很常见的现象。这两个 Fragment 都需要定义某种接口描述, 并且所有者 Activity 必须将两者绑定在一起。此外,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。

 可以使用 ViewModel 对象解决这一常见的难点。这两个 fragment 可以使用其 activity 范围共享 ViewModel 来处理此类通信.

2.4.1 共享 ViewModel 定义

class ShareViewModel : ViewModel() {
    val message =  MutableLiveData<String>()
}
复制代码

2.4.2 发送端修改数据

class SenderFragment : Fragment() {
    companion object {
        fun newInstance() = SenderFragment()
    }

    //Fragment 使用 by activityViewModels(), 需要引入 fragment-ktx
    private val viewModel: ShareViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.sender_fragment, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val message = "Hi, kejiyuanren"
        Log.d(TAG, "SenderFragment sender : name = $message")
        viewModel.message.value = message
    }
}
复制代码

2.4.3 接收端监听数据变更

class ReceiverFragment : Fragment() {
    companion object {
        fun newInstance() = ReceiverFragment()
    }

    //Fragment 使用 by activityViewModels(),需要引入 fragment-ktx
    private val viewModel: ShareViewModel by activityViewModels()

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        return inflater.inflate(R.layout.receiver_fragment, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        viewModel.message.observe(viewLifecycleOwner) { name ->
            Log.d(TAG, "ReceiverFragment receiver : name = $name")
        }
    }
}
复制代码

2.4.4 Log 输出

SenderFragment sender : name = Hi, kejiyuanren
ReceiverFragment receiver : name = Hi, kejiyuanren
复制代码

 请注意,这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 ViewModelProvider 时,它们会收到相同的 ShareViewModel 实例(其范围限定为该 Activity)。

 此方法具有以下优势:

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

2.5 将加载器替换为 ViewModel

 使用 ViewModel 替换 CursorLoader 可将界面控制器与数据加载操作分离,类之间的强引用更少。

2.5.1 CursorLoader 加载数据

 在使用加载器的一种常见方法中,应用可能会使用 CursorLoader 观察数据库的内容。当数据库中的值发生更改时,加载器会自动触发数据的重新加载并更新界面。

CursorLoader 加载数据

2.5.2 ViewModel 加载数据

ViewModel 与 Room 和 LiveData 一起使用可替换加载器。ViewModel 确保数据在设备配置更改后仍然存在。Room 在数据库发生更改时通知 LiveDataLiveData 进而使用修订后的数据更新界面。

ViewModel 加载数据

3.原理

3.1 ViewModel 结构

  • ViewModel:抽象类,有 onClear() 方法以供重写,自定义数据清空。
  • ViewModelStore: 持有管理 ViewModel 的 HashMap。
  • ViewModelStoreOwner: ComponentActivity 和 Fragment 实现此接口,约束ViewModelStore 的作用域。
  • ViewModelProvider: 创建 ViewModel

3.2 Activity 流程

3.2.1  Activity 关联 ViewModel 类图

Jetpack ViewModel Activity关系图

3.2.2  Activity 获取 ViewModel

Jetpack ViewModel Activity 获取 ViewModel

step 1  用户 Activity 获取 ViewModel

//通过下面的方法调用来追溯上面的流程图即可,最核心代码段:ViewModelProvider(this).get()

//方式 1, 通过创建 ViewModelProvider 来创建 ViewModel
val model: UserViewModel = ViewModelProvider(this).get(UserViewModel::class.java)
复制代码

step 2  ViewModelProvider 创建

public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    //ComponentActivity 中的 ViewModelStoreOwner 是 HasDefaultViewModelProviderFactory类型
    //因为 ComponentActivity 实现了 HasDefaultViewModelProviderFactory
    //具体实现都在 ComponentActivity 中
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
            ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
            : NewInstanceFactory.getInstance());
}

public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    mViewModelStore = store;
}
复制代码

step 3  获取 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");
    }
    //获取到 key
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}


public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    //从ViewModelStore获取ViewModel实例
    ViewModel viewModel = mViewModelStore.get(key);

    //检查给定对象是否是具有此Class表示的对象的实例
    if (modelClass.isInstance(viewModel)) {
        if (mFactory instanceof OnRequeryFactory) {
            ((OnRequeryFactory) mFactory).onRequery(viewModel);
        }
        //直接返回 ViewModelStore 中的缓存数据
        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);
    }
    //添加缓存到 ViewModelStore
    mViewModelStore.put(key, viewModel);
    return (T) viewModel;
}
复制代码

3.2.3  Activity 重建 ViewModel 依然存在

Jetpack ViewModel Activity 重建 ViewMoel 存在

step 1  Activity 获取 ViewModel 依赖什么?

public ViewModelStore getViewModelStore() {
    //activity还没关联Application,不能在onCreate之前去获取viewModel
    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;
}

@SuppressWarnings("WeakerAccess") /* synthetic access */
void ensureViewModelStore() {
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
                (NonConfigurationInstances) getLastNonConfigurationInstance();
        //如果 NonConfigurationInstances 不为空,则获取已经存在的 viewModelStore
        //从名字就可以看出,它是不收配置变化的实例
        //所以获取 ViewModel 依赖于 Activity中的 getLastNonConfigurationInstance()返回值
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
}
复制代码

step 2  Activity 中的 getLastNonConfigurationInstance() 探秘

NonConfigurationInstances mLastNonConfigurationInstances;

@Nullable
public Object getLastNonConfigurationInstance() {
    return mLastNonConfigurationInstances != null
            ? mLastNonConfigurationInstances.activity : null;
}

//追溯到赋值的函数 attach(), 那么向上溯源即可
final void attach(Context context, ActivityThread aThread,
        Instrumentation instr, IBinder token, int ident,
        Application application, Intent intent, ActivityInfo info,
        CharSequence title, Activity parent, String id,
        NonConfigurationInstances lastNonConfigurationInstances,
        Configuration config, String referrer, IVoiceInteractor voiceInteractor,
        Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    //……
    mLastNonConfigurationInstances = lastNonConfigurationInstances;
    //……
}
复制代码

step 3  Activity 中的 attach() 参数是否不受配置变更影响?

//屏幕旋转,需要重新加载 Activity
public void handleRelaunchActivity(ActivityClientRecord tmp,
        PendingTransactionActions pendingActions) {
    //继续跟进
    handleRelaunchActivityInner(r, configChanges, tmp.pendingResults, tmp.pendingIntents,
        pendingActions, tmp.startsNotResumed, tmp.overrideConfig, "handleRelaunchActivity");
    //……
}


private void handleRelaunchActivityInner(ActivityClientRecord r, int configChanges,
        List<ResultInfo> pendingResults, List<ReferrerIntent> pendingIntents,
        PendingTransactionActions pendingActions, boolean startsNotResumed,
        Configuration overrideConfig, String reason) {
    //继续跟进 1
    handleDestroyActivity(r.token, false, configChanges, true, reason);
    
    //继续跟进 2
    handleLaunchActivity(r, pendingActions, customIntent);
    //……
}

//继续跟进 1
public void handleDestroyActivity(IBinder token, boolean finishing, int configChanges,
        boolean getNonConfigInstance, String reason) {
    //继续跟进
    ActivityClientRecord r = performDestroyActivity(token, finishing,
        configChanges, getNonConfigInstance, reason);
    //……
}


ActivityClientRecord performDestroyActivity(IBinder token, boolean finishing,
        int configChanges, boolean getNonConfigInstance, String reason) {
    //入参时 getNonConfigInstance 为 ture
    if (getNonConfigInstance) {
        try {
            //------------------- 核心 ----------------
            //这才是核心,会将之前的 activity 的配置信息 重新放回到 ActivityClientRecord 中
            //因为 ActivityThread 中的 ActivityClientRecord 是不受 activity 重建的影响
            //------------------- 核心 ----------------
            r.lastNonConfigurationInstances
                    = r.activity.retainNonConfigurationInstances();
        } catch (Exception e) {
            if (!mInstrumentation.onException(r.activity, e)) {
                throw new RuntimeException(
                        "Unable to retain activity "
                        + r.intent.getComponent().toShortString()
                        + ": " + e.toString(), e);
            }
        }
    }
    //……
}

//继续跟进 2
public Activity handleLaunchActivity(ActivityClientRecord r,
        PendingTransactionActions pendingActions, Intent customIntent) {
    //继续跟进
    final Activity a = performLaunchActivity(r, customIntent);
    //……
}


private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    //跟进结束,已经回到了 Step 2 的分析
    activity.attach(appContext, this, getInstrumentation(), r.token,
        r.ident, app, r.intent, r.activityInfo, title, r.parent,
        r.embeddedID, r.lastNonConfigurationInstances, config,
        r.referrer, r.voiceInteractor, window, r.configCallback,
        r.assistToken);
    //……
}
复制代码

 ActivityThread 中的 ActivityClientRecord 的属性 lastNonConfigurationInstances 不受重建影响,在新建 activity 时,将 lastNonConfigurationInstances 入参到 attach() 方法中,所以 activity 重建后,依然能获取到之前的配置缓存 lastNonConfigurationInstances ,拿到其中的缓存 ViewModelStore 。

结论:ViewModel 不受 Actvity 配置变化影响。

3.3 Fragment 流程

3.3.1  Fragment 关联 ViewModel 类图

Jetpack ViewModel fragment关系图

3.3.2  Fragment 获取 ViewModel

Jetpack ViewModel Fragment 获取 ViewModel

step 1  Activity 启动创建 FragmentManagerViewModel

 Activity 启动时,通过其 FramgentManager 创建一个 FragmentManagerViewModel。将生成的 FragmentManagerViewModel 存储在 Activity 中的 ViewModelStore 中。

//FragmentActivity.java
private void init() {
    //继续跟进
    mFragments.attachHost(null /*parent*/);
}

//FragmentController.java
public void attachHost(@Nullable Fragment parent) {
    //继续跟进
    mHost.mFragmentManager.attachController(
            mHost, mHost /*container*/, parent);
}

//FragmentManager.java
void attachController(@NonNull FragmentHostCallback<?> host,
        @NonNull FragmentContainer container, @Nullable final Fragment parent) {
    //……
    // Get the FragmentManagerViewModel
    if (parent != null) {
        mNonConfig = parent.mFragmentManager.getChildNonConfig(parent);
    } else if (host instanceof ViewModelStoreOwner) {
        //因为是 Activity 启动,所以 parent 为空,继续跟进
        //继续跟进 1 , 查看 viewModelStore 从何而来 ? 
        //首先要找到 host 的 getViewModelStore()实现。
        ViewModelStore viewModelStore = ((ViewModelStoreOwner) host).getViewModelStore();
        //继续跟进 2 , 查看 mNonConfig 如何创建 ?
        mNonConfig = FragmentManagerViewModel.getInstance(viewModelStore);
    } else {
        mNonConfig = new FragmentManagerViewModel(false);
    }
}

//继续跟进 1 , 查看 viewModelStore 从何而来 ? 
//通过不断向上溯源,上面的 host 就是 FragmentActivity 中的 HostCallbacks

//FragmentActivity.java
class HostCallbacks extends FragmentHostCallback<FragmentActivity> implements
        ViewModelStoreOwner,
        OnBackPressedDispatcherOwner,
        ActivityResultRegistryOwner,
        FragmentOnAttachListener {
    public HostCallbacks() {
        super(FragmentActivity.this /*fragmentActivity*/);
    }
    
    @Override
    public ViewModelStore getViewModelStore() {
        //可以看到最终用的还是 Activity 中的 ViewModelStore
        return FragmentActivity.this.getViewModelStore();
    }
}

//继续跟进 2 , 查看 mNonConfig 如何创建 ?

//FragmentManagerViewModel.java

static FragmentManagerViewModel getInstance(ViewModelStore viewModelStore) {
    //创建 构造器 ViewModelProvider
    ViewModelProvider viewModelProvider = new ViewModelProvider(viewModelStore,
            FACTORY);
    //这个就很熟悉了,创建 FragmentManagerViewModel 对象,并加入到 viewModelStore 中
    return viewModelProvider.get(FragmentManagerViewModel.class);
}
复制代码

step 2  Fragment 启动创建 FragmentManagerViewModel

//Fragment.java
void performAttach() {
    //继续跟进
    mChildFragmentManager.attachController(mHost, createFragmentContainer(), this);
}

//FragmentManager.java
void attachController(@NonNull FragmentHostCallback<?> host,
        @NonNull FragmentContainer container, @Nullable final Fragment parent) {
    //……
    // Get the FragmentManagerViewModel
    if (parent != null) {
        //继续跟进
        //parent 就是当前 fragment, 获取 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);
    }
}


private FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
    //继续跟进
    //mNonConfig 是 FragmentManagerViewModelView
    return mNonConfig.getChildNonConfig(f);
}

//FragmentManagerViewModel.java
FragmentManagerViewModel getChildNonConfig(@NonNull Fragment f) {
    //从父 FragmentManagerViewModel 中 获取 mChildNonConfigs,存在则返回
    FragmentManagerViewModel childNonConfig = mChildNonConfigs.get(f.mWho);
    if (childNonConfig == null) {
        //不存在,则创建,并存入 mChildNonConfigs 中
        childNonConfig = new FragmentManagerViewModel(mStateAutomaticallySaved);
        mChildNonConfigs.put(f.mWho, childNonConfig);
    }
    return childNonConfig;
}
复制代码

step 3  Fragment 创建 ViewModelStore

//Fragment.java
@Override
public ViewModelStore getViewModelStore() {
    if (mFragmentManager == null) {
        throw new IllegalStateException("Can't access ViewModels from detached fragment");
    }
    if (getMinimumMaxLifecycleState() == Lifecycle.State.INITIALIZED.ordinal()) {
        throw new IllegalStateException("Calling getViewModelStore() before a Fragment "
                + "reaches onCreate() when using setMaxLifecycle(INITIALIZED) is not "
                + "supported");
    }
    //继续跟踪
    return mFragmentManager.getViewModelStore(this);
}

//FragmentManager.java
ViewModelStore getViewModelStore(@NonNull Fragment f) {
    //继续跟踪
    //mNonConfig 为 FragmentManagerViewModel
    return mNonConfig.getViewModelStore(f);
}

//FragmentManagerViewModel.java
ViewModelStore getViewModelStore(@NonNull Fragment f) {
    ViewModelStore viewModelStore = mViewModelStores.get(f.mWho);
    //存在则返回,不存在则创建
    if (viewModelStore == null) {
        viewModelStore = new ViewModelStore();
        //将创建好的 viewModelStore 存入 FragmentManagerViewModel 的 mViewModelStores 中
        mViewModelStores.put(f.mWho, viewModelStore);
    }
    return viewModelStore;
}
复制代码

3.3.3  Fragment 重建 ViewModel 依然存在

Jetpack ViewModel Fragment 重建 ViewMoel 存在

 可以看到 Activity 中的 ViewModelStore 会持有一个根的 FragmentManagerViewModel ,而Fragment 的 FragmentManagerViewModel 树都挂在了 根的 FragmentManagerViewModel。Activity 中 ViewModelStore不会因配置改变而销毁,Fragment 中 ViewModel 也不会因配置改变而销毁。

3.3.4  Fragment 可以共享 ViewModel

 Fragment 之间想共享 ViewModel,只能在 Activity 中的 ViewModelStore 下添加 ViewModel。这两个 Fragment 各自获取 ViewModelProvider 时,它们会收到相同的 ViewModel 实例(作用域为该 Activity)。

 在上面的 【Fragment 共享数据】 的例子中,使用的是 fragment-ktx 中的 by activityViewModels() 。如果直接使用 ViewModelProvider 的构造方法,需要传入 requireActivity()

4.小结

  • ViewModel绝不能引用视图、Lifecycle 可能存储对 Activity 上下文的引用的任何类。ViewModel 的存活时间更长,如果需要 Applicaiton 的上下文,使用 AndroidViewModel 即可。
  • ViewModel作为数据持有者,能够实时进行配置更改,在屏幕旋转后,自动与新 activity 绑定。不需要考虑其生命周期,同时可以避免内存泄露。
  • ViewModel 可以用来进行 activity / fragment 之间的通信,他们之间不需要相互引用,隔离依赖。

小编的博客系列

Jetpack 全家桶

猜你喜欢

转载自juejin.im/post/7018555784653094919

相关文章