Fork me on GitHub

ViewModel 源代码分析 (二)

  • viewmodel

    • CreationExtras
      ViewModel创建过程中传递额外参数的容器。

    • InitializerViewModelFactory

      ViewModelProvider.Factory接口实现类,使用ViewModelInitializer来创建ViewModel实例。

    • InitializerViewModelFactoryBuilder

      用于构建InitializerViewModelFactory的工具,通常以DSL形式提供。

    • MutableCreationExtras

      可变版本的CreationExtras,允许添加或修改额外参数。

    • ViewModelFactoryDsl

      一个DSL(领域特定语言),用于更声明式地定义如何创建ViewModel。

    • ViewModelInitializer

      用于初始化ViewModel的类,通常与ViewModelFactoryDsl一起使用。

    • InitializerViewModelFactory

      提供了使用InitializerViewModelFactory的Kotlin扩展。

  • AndroidViewModel

    AndroidViewModel是ViewModel的一个子类,它接受应用程序的Application作为上下文,这对于需要访问Application资源的ViewModel特别有用。

  • HasDefaultViewModelProviderFactory

    一个接口,标识一个类拥有默认的ViewModelProvider.Factory,用于创建ViewModel。

  • ViewModel

  • ViewModelLazy

    一个提供懒加载ViewModel实例的工具类。

  • ViewModelProvider

    用于获取ViewModel实例,确保配置更改时ViewModel可以持续使用。

  • ViewModelStore

    用于保存ViewModel实例的类,以便它们可以跨配置更改持续存在。

  • ViewModelStoreOwner

    一个接口,标识一个类可以拥有ViewModelStore。

  • ViewTreeViewModelStoreOwner

    用于从视图树中查找ViewModelStoreOwner的工具类。

  • ViewTreeViewModelKt

    提供了操作和查询视图树中ViewModel的Kotlin扩展

ViewModelStore

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package androidx.lifecycle;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Set;

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());
}

/**
* 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();
}
}

如果有不想保存的数据可以在存储前调用 ViewModelStore clear 函数

使用场景

Activity或Fragment销毁时

在Activity或Fragment的onDestroy方法中调用ViewModelStoreclear方法,可以确保所有关联的ViewModel能够及时清理资源。这是因为,在某些情况下(如用户明确退出Activity或Fragment),保留ViewModel不再有意义,应该清除所有资源以避免内存泄漏。

示例代码:

1
2
3
4
5
6
7
8
javaCopy code
@Override
protected void onDestroy() {
super.onDestroy();
if (isFinishing()) {
viewModelStore.clear();
}
}

这里,isFinishing()用于检查Activity是否正在结束(用户退出或因其他原因被销毁),如果是,则清理ViewModelStore中的所有ViewModel。

在使用ViewModel时实现资源管理

如果你的应用程序具有复杂的业务逻辑,可能会创建多个ViewModel用于不同的数据处理和业务逻辑。在这种情况下,当确定某些ViewModel不再需要时,可以通过调用clear方法释放这些ViewModel占用的资源。

3. 单元测试

在进行单元测试时,特别是在测试涉及ViewModel生命周期的逻辑时,clear方法可以在测试完成后被调用来清理测试环境,保证每个测试运行的隔离性。

注意事项:

虽然clear方法的使用可以帮助管理资源,但需要谨慎使用,以避免在不适当的时机释放资源导致的应用崩溃或不稳定。通常,Android框架会自动处理ViewModel的生命周期,只有在特定情况下,如Activity或Fragment被销毁时,才需要手动调用clear方法。

ViewModelStoreOwner

1
2
3
4
5
6
7
8
9
10
package androidx.lifecycle;

import androidx.annotation.NonNull;

@SuppressWarnings("WeakerAccess")
public interface ViewModelStoreOwner {
@NonNull
ViewModelStore getViewModelStore();
}

我们来看一下到底谁是 ViewModelStoreOwner

ViewModelStoreOwner - ComponentActivity

1
2
3
4
5
6
7
8
9
10
@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;
}

ViewModelStoreOwner - Fragment

1
2
3
4
5
6
7
8
9
10
11
12
13
@NonNull
@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);
}

ViewTreeViewModelStoreOwner

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package androidx.lifecycle;

import android.view.View;
import android.view.ViewParent;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.viewmodel.R;

public class ViewTreeViewModelStoreOwner {
private ViewTreeViewModelStoreOwner() {
// No instances
}

public static void set(@NonNull View view, @Nullable ViewModelStoreOwner viewModelStoreOwner) {
view.setTag(R.id.view_tree_view_model_store_owner, viewModelStoreOwner);
}


@Nullable
public static ViewModelStoreOwner get(@NonNull View view) {
ViewModelStoreOwner found = (ViewModelStoreOwner) view.getTag(
R.id.view_tree_view_model_store_owner);
if (found != null) return found;
ViewParent parent = view.getParent();
while (found == null && parent instanceof View) {
final View parentView = (View) parent;
found = (ViewModelStoreOwner) parentView.getTag(R.id.view_tree_view_model_store_owner);
parent = parentView.getParent();
}
return found;
}
}

ViewTreeViewModelStoreOwner 与 ViewModelStoreOwner 有什么区别

ViewModelStoreOwner

  • 定义ViewModelStoreOwner是一个接口,定义了可以拥有并管理ViewModelStore的类。ViewModelStore是一个容器,用于存储和管理ViewModel实例,确保它们能够跨配置更改(如屏幕旋转)生存下来。
  • 用途:这个接口通常由Activity和Fragment实现,使它们能够持有ViewModel实例。通过这种方式,Activity或Fragment可以在内部管理其ViewModels的生命周期,确保在发生配置更改时,ViewModels不会被销毁并重新创建。
  • 实现方式:在使用ViewModel时,开发者通常不需要直接实现ViewModelStoreOwner接口。例如,AppCompatActivityFragment已经实现了这个接口,提供了对ViewModelStore的访问。

ViewTreeViewModelStoreOwner

  • 定义ViewTreeViewModelStoreOwner是与Android视图树(View Tree)相关的工具类。它提供了静态方法来从视图树中查找ViewModelStoreOwner。这是通过在视图的Tag中设置和获取ViewModelStoreOwner实现的。
  • 用途ViewTreeViewModelStoreOwner的主要用途是在视图层级中找到最近的ViewModelStoreOwner。这对于自定义视图或视图组件特别有用,这些组件需要访问ViewModel但不直接与Activity或Fragment关联。
  • 使用场景:当你有一个嵌套在深层视图层次结构中的自定义视图,并且这个视图需要访问ViewModel时,可以使用ViewTreeViewModelStoreOwner.get(view)来找到最近的ViewModelStoreOwner实例。这使得即使在复杂的视图结构中,自定义视图也能正确地管理和访问ViewModel实例。

区别

  • 作用范围ViewModelStoreOwner直接关联于可以持有ViewModelStore的实体(如Activity或Fragment),而ViewTreeViewModelStoreOwner是一个工具类,用于在视图树中查找这些实体。
  • 使用上下文ViewModelStoreOwner通常用于直接管理ViewModels的生命周期,特别是在Activity和Fragment中。ViewTreeViewModelStoreOwner则用于在视图层级中寻找ViewModelStoreOwner,便于自定义视图或组件访问ViewModels
  • 实现与调用:Activity和Fragment通常自动实现ViewModelStoreOwner接口。相反,ViewTreeViewModelStoreOwner提供了一种机制,允许在视图树中任意位置将视图与最近的ViewModelStoreOwner关联起来,而无需直接实现或管理ViewModelStoreOwner接口。

简而言之,ViewModelStoreOwner关注于拥有和管理ViewModelStore的能力,而ViewTreeViewModelStoreOwner提供了一种查找视图树中最近的ViewModelStoreOwner的方法,使得在复杂的视图结构中的组件也能访问和使用ViewModels

ViewModelStoreOwner 的 set 方法

ViewModelStoreOwner 的 set 方法是什么时候被调用的,被添加到哪个 View 的 Tag 中?

androidx.activity.ComponentActivity 中的调用时机

androidx.activity.ComponentActivity#setContentView(android.view.View)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Override
public void setContentView(@LayoutRes int layoutResID) {
initViewTreeOwners();
super.setContentView(layoutResID);
}

@Override
public void setContentView(@SuppressLint({"UnknownNullness", "MissingNullability"}) View view) {
initViewTreeOwners();
super.setContentView(view);
}

@Override
public void setContentView(@SuppressLint({"UnknownNullness", "MissingNullability"}) View view,
@SuppressLint({"UnknownNullness", "MissingNullability"})
ViewGroup.LayoutParams params) {
initViewTreeOwners();
super.setContentView(view, params);
}

androidx.activity.ComponentActivity#addContentView

1
2
3
4
5
6
7
@Override
public void addContentView(@SuppressLint({"UnknownNullness", "MissingNullability"}) View view,
@SuppressLint({"UnknownNullness", "MissingNullability"})
ViewGroup.LayoutParams params) {
initViewTreeOwners();
super.addContentView(view, params);
})

在 initViewTreeOwners 中调用 ViewModelStoreOwner 的 set

androidx.activity.ComponentActivity#initViewTreeOwners

1
2
3
4
5
6
7
8
private void initViewTreeOwners() {
// Set the view tree owners before setting the content view so that the inflation process
// and attach listeners will see them already present
ViewTreeLifecycleOwner.set(getWindow().getDecorView(), this);
ViewTreeViewModelStoreOwner.set(getWindow().getDecorView(), this);
ViewTreeSavedStateRegistryOwner.set(getWindow().getDecorView(), this);
ViewTreeOnBackPressedDispatcherOwner.set(getWindow().getDecorView(), this);
}

这里顺便提一下 ViewTreeLifecycleOwner,ViewTreeSavedStateRegistryOwner,ViewTreeOnBackPressedDispatcherOwner

在Android Jetpack架构组件中,ViewTreeLifecycleOwnerViewTreeSavedStateRegistryOwnerViewTreeOnBackPressedDispatcherOwner是用于在视图树中传递和存储与生命周期、状态保存和返回按钮事件处理相关的对象的工具类。它们允许开发者在任何视图层级中访问与这些功能相关的对象,增强了组件化和模块化开发的灵活性。

ViewTreeLifecycleOwner

ViewTreeLifecycleOwnerLifecycleOwner接口相关,后者是一个类可以实现的接口,表示这个类有一个生命周期,比如Activity或Fragment。ViewTreeLifecycleOwner提供了静态方法来在视图树中设置和获取LifecycleOwner。这使得在视图树中的任何位置,都可以访问到与之相关联的LifecycleOwner,进而可以根据生命周期事件来管理资源,比如开始/停止数据加载、绑定/解绑资源等。

ViewTreeSavedStateRegistryOwner

ViewTreeSavedStateRegistryOwnerSavedStateRegistryOwner接口相关,后者管理和存储状态,以便在进程被杀后恢复状态。通过ViewTreeSavedStateRegistryOwner,开发者可以为视图树中的任何视图设置和获取与状态保存相关的SavedStateRegistryOwner。这使得即使是自定义视图也能够利用Android的状态保存机制,无需依赖于Activity或Fragment来保存和恢复状态。

ViewTreeOnBackPressedDispatcherOwner

ViewTreeOnBackPressedDispatcherOwnerOnBackPressedDispatcherOwner接口相关,后者提供了一个组件化的方式来处理返回按钮事件。ViewTreeOnBackPressedDispatcherOwner允许开发者为视图树中的任何视图设置和获取OnBackPressedDispatcherOwner。这意味着在任何视图中,都可以独立处理返回按钮的点击事件,而不是仅限于在Activity中处理。这对于构建复杂的用户界面和处理嵌套的用户交互特别有用。

使用场景和好处

  • 组件化开发:这些工具类支持更加组件化的开发方式,视图或组件可以独立于Activity或Fragment使用这些架构组件。
  • 灵活性和可重用性:使得自定义视图和组件更加灵活,易于重用,因为它们可以在不依赖特定Activity或Fragment的情况下,管理自己的生命周期、状态保存和返回按钮处理。
  • 简化代码:简化了在自定义视图或深层嵌套的视图结构中访问这些功能的方式,避免了复杂的回调结构或过度依赖于上下文。

综上所述,ViewTreeLifecycleOwnerViewTreeSavedStateRegistryOwnerViewTreeOnBackPressedDispatcherOwner为Android开发者提供了强大的工具,以更加灵活和模块化的方式处理生命周期管理、状态保存和返回按钮事件处理。

ViewTreeViewModelKt

Google 也准备了一个 Kotlin 使用的扩展函数配合 ViewModelStoreOwner

1
2
3
4
5
6
package androidx.lifecycle

import android.view.View

public fun View.findViewTreeViewModelStoreOwner(): ViewModelStoreOwner? =
ViewTreeViewModelStoreOwner.get(this)
,