ViewModel原理

534 阅读7分钟

1. 概述

在 Android 开发中,ViewModel 的创建通常通过 ViewModelProvider 来完成。下面的示例代码展示了如何创建一个 ViewModel 实例:

示例代码:

val viewModel = ViewModelProvider(this).get(MyViewModel::class.java)

可以看到,ViewModel 的创建过程是由 ViewModelProvider 完成的。

2. ViewModelProvider

ViewModelProvider 提供了一个主构造函数和两个次构造函数,如下:

主构造函数

public open class ViewModelProvider @JvmOverloads constructor(
    private val store: ViewModelStore,
    private val factory: Factory,
    private val defaultCreationExtras: CreationExtras = CreationExtras.Empty,
)

在创建 ViewModelProvider 对象时,需要传入三个参数:ViewModelStore、ViewModelProvider.Factory 和 CreationExtras。

次构造函数

public constructor(owner: ViewModelStoreOwner) : this(
    owner.viewModelStore,
    defaultFactory(owner),
    defaultCreationExtras(owner)
)

public constructor(owner: ViewModelStoreOwner, factory: Factory) : this(
    owner.viewModelStore,
    factory,
    defaultCreationExtras(owner)
)

通常我们使用次构造函数来创建 ViewModelProvider 对象,只需传入 ViewModelStoreOwner 参数。在次构造函数中,通过 owner: ViewModelStoreOwner 获取默认的 ViewModelStore、Factory 和 CreationExtras,然后调用主构造函数创建 ViewModelProvider 对象。

ViewModelProvider#get 方法

ViewModelProvider 提供了 get 方法用于获取 ViewModel 实例,如下:

@MainThread
public open operator fun <T : ViewModel> get(modelClass: Class<T>): T {
    val canonicalName = modelClass.canonicalName
        ?: throw IllegalArgumentException("本地类和匿名类不能是 ViewModel")
        return get("$DEFAULT_KEY:$canonicalName", modelClass)
}

@MainThread
public open operator fun <T : ViewModel> get(key: String, modelClass: Class<T>): T {
    // 从 ViewModelStore 中获取与 key 对应的 ViewModel 实例
    val viewModel = store[key]

    // 如果获取的实例与 modelClass 类型匹配,则返回该实例
    if (modelClass.isInstance(viewModel)) {
        (factory as? OnRequeryFactory)?.onRequery(viewModel)
        return viewModel as T
    } else {
        @Suppress("ControlFlowWithEmptyBody")
        if (viewModel != null) {
            // TODO: 记录警告日志
        }
    }

    // 创建额外参数用于 ViewModel 的创建
    val extras = MutableCreationExtras(defaultCreationExtras)
    extras[VIEW_MODEL_KEY] = key

    // 使用 factory 创建 ViewModel 实例,并处理可能的反序列化问题
    return try {
        factory.create(modelClass, extras)
    } catch (e: AbstractMethodError) {
        factory.create(modelClass)
    }.also {
        // 将新创建的 ViewModel 实例放入存储中
        store.put(key, it)
    }
}

在 get 方法中:

  1. 尝试从 ViewModelStore 中获取与 key 对应的 ViewModel 实例。
  2. 如果获取的 ViewModel 实例与 modelClass 类型匹配,则返回该实例。
  3. 如果获取的 ViewModel 实例与 modelClass 类型不匹配,则使用 ViewModelProvider.Factory 创建一个新的 ViewModel 实例。

到这里,我们大概可以理解 ViewModelProvider 主构造函数中三个参数的作用:

  • ViewModelStore:用于存储和管理 ViewModel 实例。
  • ViewModelProvider.Factory:用于创建 ViewModel 实例的工厂类。
  • CreationExtras:用于在创建 ViewModel 时传递额外参数。

接下来我们将深入分析 ViewModelStore、ViewModelProvider.Factory 和 CreationExtras 的创建过程及其内部实现。

3. ViewModelStore 创建过程及介绍

创建过程

在 ViewModelProvider 的次构造函数中,ViewModelStore 是通过 ViewModelStoreOwner 接口的 getViewModelStore 方法获取的。如下:

public constructor(
    owner: ViewModelStoreOwner
) : this(owner.viewModelStore, defaultFactory(owner), defaultCreationExtras(owner))

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

ComponentActivity 实现了 ViewModelStoreOwner 接口,如下:

public class ComponentActivity implements ViewModelStoreOwner {
    private ViewModelStore mViewModelStore;

    @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;
    }

    void ensureViewModelStore() {
        if (mViewModelStore == null) {
            NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                // Restore the ViewModelStore from NonConfigurationInstances
                mViewModelStore = nc.viewModelStore;
            }
            if (mViewModelStore == null) {
                mViewModelStore = new ViewModelStore();
            }
        }
    }

    /**
     * 当 Activity 即将因为配置更改(如屏幕旋转、语言改变等)而被销毁时,
     * 系统会调用 onRetainNonConfigurationInstance。
     * 该方法允许 Activity 保存一些状态,以便在新创建的 Activity 实例中恢复这些状态。
     */
    @Override
    @Nullable
    @SuppressWarnings("deprecation")
    public final Object onRetainNonConfigurationInstance() {
        Object custom = onRetainCustomNonConfigurationInstance();
        ViewModelStore viewModelStore = mViewModelStore;
        if (viewModelStore == null) {
            NonConfigurationInstances nc = (NonConfigurationInstances) getLastNonConfigurationInstance();
            if (nc != null) {
                viewModelStore = nc.viewModelStore;
            }
        }
        if (viewModelStore == null && custom == null) {
            return null;
        }
        NonConfigurationInstances nci = new NonConfigurationInstances();
        nci.custom = custom;
        nci.viewModelStore = viewModelStore;
        return nci;
    }
}

可以发现,Activity 中创建 ViewModelStore 的过程如下:

  1. NonConfigurationInstances 中取出 viewModelStore
  2. 如果取出的 viewModelStore 为空,则创建 ViewModelStore
  3. 当 Activity 即将因为配置更改(如屏幕旋转、语言改变等)而被销毁时,系统会调用 onRetainNonConfigurationInstance
  4. onRetainNonConfigurationInstance 中,将 viewModelStore 保存到 NonConfigurationInstances 中。

ViewModelStore 介绍

ViewModelStore 类用于存储和管理 ViewModel 实例:

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

    public final void clear() {
        for (ViewModel vm : mMap.values()) {
            vm.onCleared();
        }
        mMap.clear();
    }
}

关键点总结:

  • ViewModelStore 通过 HashMap 存储 ViewModel 实例。
  • put 方法用于存储 ViewModel,如果同一个 key 已经存在旧的 ViewModel 实例,会调用旧实例的 onCleared 方法。
  • get 方法用于获取指定 key 的 ViewModel 实例。
  • clear 方法清除所有存储的 ViewModel 实例,并调用它们的 onCleared 方法。

4. ViewModelProvider.Factory

创建过程

在 ViewModelProvider 的次构造函数中,调用了 defaultFactory 方法获取默认的 Factory 实例。defaultFactory 方法位于 ViewModelProvider 的内部类 AndroidViewModelFactory 中,如下:

internal fun defaultFactory(owner: ViewModelStoreOwner): Factory =
    if (owner is HasDefaultViewModelProviderFactory)
        owner.defaultViewModelProviderFactory 
    else instance

defaultFactory 方法中,首先判断 owner 是否实现了 HasDefaultViewModelProviderFactory 接口。如果实现了,则直接返回 ownerdefaultViewModelProviderFactory 属性。否则,返回一个默认的 Factory 实例。

当在 Activity 中创建 ViewModel 时,Activity 实现了 HasDefaultViewModelProviderFactory 接口,因此会返回 Activity 的 defaultViewModelProviderFactory 属性,如下:

public interface HasDefaultViewModelProviderFactory {

    @NonNull
    ViewModelProvider.Factory getDefaultViewModelProviderFactory();

    @NonNull
    default CreationExtras getDefaultViewModelCreationExtras() {
        return CreationExtras.Empty.INSTANCE;
    }
}

public class ComponentActivity implements HasDefaultViewModelProviderFactory {

    @NonNull
    @Override
    public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {
        if (mDefaultFactory == null) {
            mDefaultFactory = new SavedStateViewModelFactory(
                getApplication(),
                this,
                getIntent() != null ? getIntent().getExtras() : null
            );
        }
        return mDefaultFactory;
    }
}

在上面分析过,ViewModelProvider 的 get 方法中,通过 factorycreate 方法创建 ViewModel 实例。那么如果是在 Activity 中创建 ViewModel,就会调用 SavedStateViewModelFactorycreate 方法,如下:

class SavedStateViewModelFactory : ViewModelProvider.OnRequeryFactory, ViewModelProvider.Factory {
    private var application: Application? = null
    private val factory: ViewModelProvider.Factory

    constructor(application: Application?, owner: SavedStateRegistryOwner, defaultArgs: Bundle?) {
        this.application = application
        factory = if (application != null) getInstance(application)
        else ViewModelProvider.AndroidViewModelFactory()
    }

    override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
        val key = extras[ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY]
            ?: throw IllegalStateException(
                "VIEW_MODEL_KEY must always be provided by ViewModelProvider"
            )

        return if (extras[SAVED_STATE_REGISTRY_OWNER_KEY] != null &&
            extras[VIEW_MODEL_STORE_OWNER_KEY] != null) {
            val application = extras[ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY]
            
            // 是否是AndroidViewModel
            val isAndroidViewModel = AndroidViewModel::class.java.isAssignableFrom(modelClass)

            val constructor: Constructor<T>? = if (isAndroidViewModel && application != null) {
                // 用于确定ViewModel构造函数中是否有SavedStateHandle
                findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE)
            } else {
                // 用于确定ViewModel构造函数中是否有SavedStateHandle
                findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE)
            }

            // 如果ViewModel没有使用SavedStateHandle
            if (constructor == null) {
                // 使用AndroidViewModelFactory创建ViewModel
                return factory.create(modelClass, extras)
            }

            if (isAndroidViewModel && application != null) {
                newInstance(modelClass, constructor, application, extras.createSavedStateHandle())
            } else {
                newInstance(modelClass, constructor, extras.createSavedStateHandle())
            }
        } else {
            throw IllegalStateException(
                "SAVED_STATE_REGISTRY_OWNER_KEY and VIEW_MODEL_STORE_OWNER_KEY must be provided in the creation extras to successfully create a ViewModel."
            )
        }
    }
}

create 方法中,首先判断 ViewModel 是否使用了 SavedStateHandle。如果使用了,则通过 SavedStateViewModelFactorynewInstance 方法创建 ViewModel 实例;否则,使用 AndroidViewModelFactory 创建 ViewModel 实例。

SavedStateViewModelFactorynewInstance 方法如下:

internal fun <T : ViewModel?> newInstance(
    modelClass: Class<T>,
    constructor: Constructor<T>,
    vararg params: Any
): T {
    return try {
        constructor.newInstance(*params)
    } catch (e: IllegalAccessException) {
        // 处理异常
    }
}

AndroidViewModelFactorycreate 方法如下:

@Suppress("DocumentExceptions")
override fun <T : ViewModel> create(modelClass: Class<T>, extras: CreationExtras): T {
    return if (application != null) {
        modelClass.getConstructor(Application::class.java).newInstance(app)
    } else {
        val application = extras[APPLICATION_KEY]
        if (application != null) {
            modelClass.getConstructor(Application::class.java).newInstance(application)
        } else {
            modelClass.newInstance()
        }
    }
}

关键点总结:

  1. defaultFactory 方法中,判断 owner 是否实现了 HasDefaultViewModelProviderFactory 接口,决定返回的 Factory 实例。
  2. 如果是在 Activity 中创建 ViewModel,通常会调用 SavedStateViewModelFactorycreate 方法创建 ViewModel 实例。
  3. SavedStateViewModelFactorycreate 方法会根据 ViewModel 是否需要 SavedStateHandle 来决定使用哪种方式创建实例。
  4. 如果不需要 SavedStateHandle,则使用 AndroidViewModelFactory 创建 ViewModel 实例。
  5. 最终都会通过反射创建ViewModel实例。

5. CreationExtras 创建过程及介绍

创建过程

CreationExtras 是在创建 ViewModel 时传递额外参数的一个对象。在 ViewModelProvider 的主构造函数中,默认传入的是 CreationExtras.Empty。当使用次构造函数时,会通过 defaultCreationExtras 方法生成相应的 CreationExtras 实例。

ViewModelProvider 类中,defaultCreationExtras 方法的定义如下:

internal fun defaultCreationExtras(owner: ViewModelStoreOwner): CreationExtras {
    if (owner is HasDefaultViewModelProviderFactory) {
        return owner.defaultViewModelCreationExtras
    }
    return CreationExtras.Empty
}

public class ComponentActivity implements HasDefaultViewModelProviderFactory {
    public CreationExtras getDefaultViewModelCreationExtras() {
        MutableCreationExtras extras = new MutableCreationExtras();
        if (getApplication() != null) {
            extras.set(ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY, getApplication());
        }
        extras.set(SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY, this);
        extras.set(SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY, this);
        if (getIntent() != null && getIntent().getExtras() != null) {
            extras.set(SavedStateHandleSupport.DEFAULT_ARGS_KEY, getIntent().getExtras());
        }
        return extras;
    }
}

CreationExtras的创建过程和ViewModelProvider.Factory的创建过程类似。

CreationExtras 介绍

CreationExtras 是一个接口,它用于在创建 ViewModel 时提供额外的参数。以下是 CreationExtras 的定义:

public interface CreationExtras {
    CreationExtras Empty = new CreationExtras() {
        // Empty implementation
    };

    // 可以添加需要的键值对等方法
}

自定义 CreationExtras

当需要传递额外参数时,可以自定义实现 CreationExtras 接口。以下是一个自定义实现的示例:

public class MyCreationExtras implements CreationExtras {
    private final Map<String, Object> extras = new HashMap<>();

    public void put(String key, Object value) {
        extras.put(key, value);
    }

    public Object get(String key) {
        return extras.get(key);
    }
}

在创建 ViewModel 时,可以将 MyCreationExtras 传递给 ViewModelProvider

使用示例

以下是如何使用自定义 CreationExtras 的示例:

MyCreationExtras extras = new MyCreationExtras();
extras.put("my_key", "my_value");

ViewModelProvider.Factory factory = new ViewModelProvider.NewInstanceFactory();
ViewModelProvider provider = new ViewModelProvider(this, factory, extras);

MyViewModel viewModel = provider.get(MyViewModel.class);

在这个示例中,我们创建了一个 MyCreationExtras 实例并添加了一些参数,然后将其传递给 ViewModelProvider,最终创建了一个 MyViewModel 实例。

总结

本文深入分析了 ViewModel 在 Android 中的创建过程,详细介绍了 ViewModelProvider、ViewModelStore、ViewModelProvider.Factory 以及 CreationExtras 的实现和作用。通过对示例代码的剖析,我们可以清晰地了解以下内容:

  1. ViewModelProvider:通过主构造函数和次构造函数创建 ViewModelProvider 对象,传递必要的参数(ViewModelStore、ViewModelProvider.Factory 和 CreationExtras),实现了对 ViewModel 实例的管理和提供。
  2. ViewModelStore:用于存储和管理 ViewModel 实例,确保在配置更改时(如屏幕旋转)能正确地保留和恢复 ViewModel。
  3. ViewModelProvider.Factory:负责创建 ViewModel 实例,支持带有 SavedStateHandle 的 ViewModel,确保在特定场景下(如需要保存状态)能够正确创建 ViewModel 实例。
  4. CreationExtras:用于在创建 ViewModel 时传递额外的参数,提供灵活的扩展能力,满足不同场景下的需求。

通过本文的分析,读者可以更深入地理解 ViewModel 的创建过程及其内部机制,为在实际开发中更好地使用 ViewModel 提供了理论基础和实践指导。