ViewModel源码分析

2023-05-16

首先,还是先看一个例子:

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.
    }
}
public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class);
    }
}

该例子定义了一个ViewModel的子类MyViewModel,然后通过ViewModelProvider的实例方法get()获取到MyViewModel的实例。

ViewModelProvider

/**

 * Creates {@code ViewModelProvider}. This will create {@code ViewModels}
 * and retain them in a store of the given {@code ViewModelStoreOwner}.
 * <p>
 * This method will use the
 * {@link HasDefaultViewModelProviderFactory#getDefaultViewModelProviderFactory() default factory}
 * if the owner implements {@link HasDefaultViewModelProviderFactory}. Otherwise, a
 * {@link NewInstanceFactory} will be used.
 */
public ViewModelProvider(@NonNull ViewModelStoreOwner owner) {
    this(owner.getViewModelStore(), owner instanceof HasDefaultViewModelProviderFactory
         ? ((HasDefaultViewModelProviderFactory) owner).getDefaultViewModelProviderFactory()
         : NewInstanceFactory.getInstance());
}
/**
 * Creates {@code ViewModelProvider}, which will create {@code ViewModels} via the given
 * {@code Factory} and retain them in the given {@code store}.
 *
 * @param store   {@code ViewModelStore} where ViewModels will be stored.
 * @param factory factory a {@code Factory} which will be used to instantiate
 *                new {@code ViewModels}
 */
public ViewModelProvider(@NonNull ViewModelStore store, @NonNull Factory factory) {
    mFactory = factory;
    mViewModelStore = store;
}

从构造方法中可以看出ViewModelProvider需要ViewModelStore和Factory两个类型的成员变量才能构造处理,分别是mViewModelStore和mFactory,ComponentActivity和Fragment分别都实现了ViewModelStoreOwner和HasDefaultViewModelProviderFactory接口,所以都可以从中获取到ViewModelStore和Factory的实例。

@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");
    }
    return get(DEFAULT_KEY + ":" + canonicalName, modelClass);
}
@MainThread
public <T extends ViewModel> T get(@NonNull String key, @NonNull Class<T> modelClass) {
    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()方法首先尝试通过mViewModelStore的get()方法获取ViewModel的实例,如果没获取到则使用mFactory的create()创建实例,创建出来后则存入到mViewModelStore中。在这里mFactory就是ViewModel的构造工厂,mViewModelStore则是ViewModel的缓存管理者。

ViewModelProvider作为ViewModel的提供者,使用缓存mViewModelStore和工厂mFactory实现,第一次提供ViewModel时会通过工厂创建出来,后续则都是从缓存中拿。

ViewModelStore

public ComponentActivity() {

        ...
        getLifecycle().addObserver(new LifecycleEventObserver() {
        @Override
        public void onStateChanged(@NonNull LifecycleOwner source,
                                   @NonNull Lifecycle.Event event) {
            if (event == Lifecycle.Event.ON_DESTROY) {
                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.");
    }
    if (mViewModelStore == null) {
        NonConfigurationInstances nc =
            (NonConfigurationInstances) getLastNonConfigurationInstance();
        if (nc != null) {
            // Restore the ViewModelStore from NonConfigurationInstances
            mViewModelStore = nc.viewModelStore;
        }
        if (mViewModelStore == null) {
            mViewModelStore = new ViewModelStore();
        }
    }
    return mViewModelStore;
}

ViewModelStoreOwner接口getViewModelStore()的实现就是提供一个ViewModelStore实例,而ComponentActivity使用Lifecycle能力在页面销毁时调用ViewModelStore实例的clear方法,清空其中的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());
    }

    /**
     *  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中使用HashMap管理ViewModel缓存,它被页面持有,并在页面真正销毁时才清空缓存。

官网的这张图中可以说明ViewModel的生命周期。

图片

SaveStateViewModelFactory


public ViewModelProvider.Factory getDefaultViewModelProviderFactory() {

    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 (mDefaultFactory == null) {
        mDefaultFactory = new SavedStateViewModelFactory(
            getApplication(),
            this,
            getIntent() != null ? getIntent().getExtras() : null);
    }
    return mDefaultFactory;
}

ComponentActivity中getDefaultViewModelProviderFactory()方法通过构造方法创建一个SavedStateViewModelFactory对象,传入了Application、当前ComponentActivity实例和Intent中的数据bundle。

SavedStateViewModelFactory构造方法


public SavedStateViewModelFactory(@NonNull Application application,
                                  @NonNull SavedStateRegistryOwner owner,
                                  @Nullable Bundle defaultArgs) {
    mSavedStateRegistry = owner.getSavedStateRegistry();
    mLifecycle = owner.getLifecycle();
    mDefaultArgs = defaultArgs;
    mApplication = application;
    mFactory = ViewModelProvider.AndroidViewModelFactory.getInstance(application);
}

构造方法接受的参数中,页面实例是SavedStateRegistryOwner接口类型的,通过该接口获取到SavedStateRegistry和Lifecycle。另外成员变量mFactory是AndroidViewModelFactory的单例对象。

SavedStateViewModelFactory的create()

@Override
public <T extends ViewModel> T create(@NonNull String key, @NonNull Class<T> modelClass) {
    boolean isAndroidViewModel = AndroidViewModel.class.isAssignableFrom(modelClass);
    Constructor<T> constructor;
    if (isAndroidViewModel) {
        constructor = findMatchingConstructor(modelClass, ANDROID_VIEWMODEL_SIGNATURE);
    } else {
        constructor = findMatchingConstructor(modelClass, VIEWMODEL_SIGNATURE);
    }
    // doesn't need SavedStateHandle
    if (constructor == null) {
        return mFactory.create(modelClass);
    }

    SavedStateHandleController controller = SavedStateHandleController.create(
        mSavedStateRegistry, mLifecycle, key, mDefaultArgs);
    try {
        T viewmodel;
        if (isAndroidViewModel) {
            viewmodel = constructor.newInstance(mApplication, controller.getHandle());
        } else {
            viewmodel = constructor.newInstance(controller.getHandle());
        }
        viewmodel.setTagIfAbsent(TAG_SAVED_STATE_HANDLE_CONTROLLER, controller);
        return viewmodel;
    } catch (IllegalAccessException e) {
        throw new RuntimeException("Failed to access " + modelClass, e);
    } catch (InstantiationException e) {
        throw new RuntimeException("A " + modelClass + " cannot be instantiated.", e);
    } catch (InvocationTargetException e) {
        throw new RuntimeException("An exception happened in constructor of "
                                   + modelClass, e.getCause());
    }
}

create()方法支持创建三种类型的ViewModel:AndroidViewModel、支持SavedState的ViewModel、普通ViewModel,这里由于篇幅原因,只分析一下普通ViewModel的创建。普通ViewModel通过mFactory的create()方法创建出来。

AndroidViewModelFactory的create()

public static class AndroidViewModelFactory extends ViewModelProvider.NewInstanceFactory {

    ...
    @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);
            } catch (NoSuchMethodException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (IllegalAccessException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InstantiationException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            } catch (InvocationTargetException e) {
                throw new RuntimeException("Cannot create an instance of " + modelClass, e);
            }
        }
        return super.create(modelClass);
    }
}

AndroidViewModelFactory的create()方法判断如果不是AndroidViewModel类型,就直接通过父类的create()方法创建,而AndroidViewModelFactory的父类是NewInstanceFactory。

NewInstanceFactory的create()

public static class NewInstanceFactory implements Factory {
    ...

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

NewInstanceFactory的create()则是直接通过反射创建出ViewModel实例。

SaveStateViewModelFactory作为ComponentActivity和Fragment提供的对象,在NewInstanceFactory的基础上增加了对AndroidViewModel和支持SavedStated的ViewModel的创建,但对于普通的ViewModel创建,最后还是降级使用NewInstanceFactory完成。

到此,ViewModel的主要类已经分析完了,接下来可以结合类关系,一定程度上总结出对全局视角的理解。

图片

 

主要类说明:

  • ViewModelProvider:ViewModel提供者

  • ViewModelStore:ViewModel缓存管理

  • ViewModelProvider.Factory:ViewModel创建工厂

  • SavedStateViewModelFactory:ViewModel创建工厂的实现

  • NewInstanceFactory:普通ViewModel创建工厂的实现

类关系描述:

ViewModel通过ViewModelProvider的get()方法获取到,ViewModelProvider由缓存ViewModelStore和创建工厂ViewModelProvider.Factory组合而成,ViewModelStore和ViewModelProvider.Factory也是ComponentActivity的一部分,ComponentActivity通过实现ViewModelStoreOwner和HasDefaultViewModelProviderFactory两个接口对外提供ViewModelStore和ViewModelProvider.Factory。其中,ViewModelProvider.Factory在ComponentActivity的具体实现是SavedStateViewModelFactory,SavedStateViewModelFactory一部分由AndroidViewModelFactory组合而成,它提供创建三种ViewModel的能力,其中普通ViewModel的创建是由AndroidViewModelFactory的父类NewInstanceFactory完成。

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

ViewModel源码分析 的相关文章

  • ContextMenu 命令绑定到父级和自身

    我正在尝试在具有多个命令绑定的 DataGrid 上创建一个上下文菜单 有些必须绑定到本地 ViewModel 即 Row 的 ViewModel 有些必须绑定到父 ViewModel 到目前为止 我没有运气遵循其他解决方案 我只执行子命令
  • Asp.net MVC Razor 页面上有多个表单

    Yo 我的网站上有一个注册页面 页面顶部是现有用户的登录表单 主区域有登记表 登录区域是部分视图 model ViewModels LoginViewModel注册区域也是部分的 model ViewModels RegViewModel
  • MVC 映射到模型中可为 null 的 bool

    使用包含该字段的视图模型 public bool IsDefault get set 尝试在视图中映射时出现错误 无法隐式转换类型 bool 布尔 存在显式转换 您是否缺少强制转换 我尝试过铸造并使用 Value但都不起作用 请注意 我想要
  • ASP.NET MVC 2 - ViewModel 前缀

    我想在我的视图中使用 RenderPartial 两次 并关联不同的模型 问题是两个模型中都存在某些属性 昵称 密码 它们没有前缀 因此即使 id 或名称在输出中也是相同的 现在 如果我的昵称或密码有模型错误 两个字段都会突出显示 主视图
  • ViewModel 还是 ViewBag?

    我对 MVC4 EF5 和 ASP Net 相当陌生 而且我似乎无法在任何地方找到好的答案 基本上 一切都应该通过视图模型完成还是也可以合并视图包 假设我有一个填充下拉列表的方法 并且我正在使用视图模型来表示视图的输出 我可以使用吗View
  • 我的视图模型值类型属性应该可以为空吗?

    我已经让我的视图模型属性可以为空已经有一段时间了 我的理由是 当进行验证时 我不希望将默认值插入到用户留空的字段中 但这是必需的 当然 我将必填字段标记为必填 但这让我认为这样做会失去对象模型的大量保真度 当然 我的域类只有在实际上可以为空
  • 模型将回发数据绑定到类型为 List 的控制器操作参数

    我对类型有很强的类型观 List
  • 如何使用 Silverlight 和 MVVM 设计复合视图和视图模型?

    我想在我的 Silverlight MVVM 应用程序中创建一个 向导 该向导应包含多个步骤 您可以使用 下一个 和 上一个 在这些步骤之间导航 我面临的问题是视图和视图模型之间的关系 我希望向导本身有一个视图和视图模型 我的直觉告诉我 向
  • Kotlin - 在运行函数之前等待观察多个 LiveData

    我正在使用 viewModel 从房间数据库中提取实时数据 我有 2 个从 viewModel 中提取的 LiveData 然后我将运行一个函数从我的服务器中提取数据 我需要在运行从服务器获取信息的函数之前设置这两个值 因为这些值是帖子正文
  • MVC 中的 ViewModel 和与实体框架的一对多关系?

    我有一个用于在数据库中存储有关顾问的信息的应用程序 该模型是一个实体框架模型 数据库表与许多其他表 工作经验 计划 能力区域等 具有一对多关系 现在 当我想在视图中创建一个新的 Consultant 对象时 我实际上只想将 Consulta
  • 为什么在回调中调用 ViewModel 时会发生重组?

    我完全混淆了撰写概念 我有一个代码 Composable fun HomeScreen viewModel HomeViewModel getViewModel Scaffold val isTimeEnable by viewModel
  • 为什么 Android 中需要视图模型工厂?

    我们一直在讨论这个问题 但不知道为什么要创建viewmodel工厂来创建viewmodel而不是直接实例化viewmodel 创建一个只创建视图模型的工厂有什么好处 我只是举了一个简单的例子来说明我如何在没有工厂的情况下做到这一点 这是 k
  • 在kotlin中如何使用ViewModel和ViewModelProvider.AndroidViewModelFactory?

    在我当前的项目中 我使用下一行 mViewModel ViewModelProviders of this get MainViewModel class java 例如一个ViewModel but in https developer
  • asp.net mvc 模型与实体框架模型

    在 asp net mvc model 文件夹中创建模型是最佳实践吗 将这些模型与您的视图结合使用 并使用服务层将我的模型 调整 为 EF 模型 或者您是否使用过其他方法 这种方法的问题在于 大多数时候我的 自制 模型是 EF 模型的副本
  • MVVM:如何将参数传递给 ViewModel 的构造函数

    我正在使用 L Bugnion 的 MVVM Light 框架 将参数 例如 Customers IS 传递给 ViewModel 构造函数的推荐方法有哪些 编辑 每个 ViewModel 所需的参数不是跨模型共享的参数 它对于每个视图模型
  • 聚合 ViewModel 是个好主意吗?

    将一个 ViewModel 作为另一个 ViewModel 的属性是好还是坏主意 例如 public sealed class ContentManagerViewModel ViewModel public FindViewModel F
  • 两个视图 - 一个 ViewModel

    我一定是误解了这个概念ViewModels和意见 但目前我无法从地面重建应用程序 这次做得更好 我的情况是 我有一个视图 用户可以在其中加载文件并读取它们 绘图仪显示图形并实现一些操作 我希望能够生成数据报告 如摘要 但我希望它在其他视图中
  • 在 mvc3 中公开实体或 DTO 以查看的最佳实践是什么?

    我创建了自己定制的大量架构 包括针对不同技术的 n 层 目前正在使用 asp net mvc 框架进行 n 层架构 问题是我在数据访问层有实体框架 由于实体将拥有所有关系元数据和导航属性 因此它变得更重 我觉得直接通过 mvc 视图公开这些
  • 将显示的模式(路径)绑定到数据模型/如何使路径具有相同的维度

    背景 随意跳过此 我正在构建一个处理马匹 马匹主人以及主人的比赛颜色 丝绸 的程序 这个问题是关于一个UserControl 称为SilksControl充当视图JockeySilks 为了表示丝绸 我使用以下枚举类 public clas
  • Room - LiveData 观察器在数据库更新时不会触发

    我试图在下面的代码中找出 为什么在我用新数据填充数据库后 Room 的 LiveData observable 不会给我新的转变 这是放在我的活动的 onCreate 方法中 shiftsViewModel ViewModelProvide

随机推荐

  • typedef的用法

    typedef中声明的类型在变量名的位置出现 什么意思呢 xff0c 我们回头来看 我们是怎么声明int类型变量的 xff1f int Typename 像上面这样 xff0c 对不对 xff1f 那么用typedef之后呢 xff1f 把
  • Activity启动流程(一)

    Launcher进程请求AMSAMS发送创建应用进程请求Zygote进程接受请求并孵化应用进程应用进程启动ActivityThread 一 Launcher进程请求AMS 上面我们提到根Activity的启动流程其实就是桌面上点击一个应用图
  • Activity启动流程(二)

    应用进程绑定到AMSAMS发送启动Activity的请求ActivityThread的Handler处理启动Activity的请求 一 应用进程绑定到AMS 1 时序图 2 详细过程 在前面一篇我们知道当Zygote进程孵化出应用进程后会执
  • AudioRecord

    数字音频 数字音频通常分为三步 xff1a 采样 量化 编码 采样 xff1a 就是将获取的信号给数字化 xff0c 其中有个概念就是采样频率 xff0c 而人耳能听到的频率范围只有20Hz 20kHz xff0c 所以一般设置的都是44
  • GCC编译C/C++程序(一步完成)

    使用 GCC 编译器编译 C 或者 C 43 43 程序 xff0c 也必须要经历这 4 个过程 但考虑在实际使用中 xff0c 用户可能并不关心程序的执行结果 xff0c 只想快速得到最终的可执行程序 xff0c 因此 gcc 和 g 4
  • GCC -E选项:对源程序做预处理操作

    存储在 demo c 文件中 include lt stdio h gt int main puts 34 hello world 34 return 0 通过为 gcc 指令添加 E 选项 xff0c 即可控制 GCC 编译器仅对源代码做
  • GCC -S选项:编译非汇编文件

    root 64 bogon demo cat demo c include lt stdio h gt int main puts 34 Hello World 34 return 0 root 64 bogon demo gcc E de
  • GCC -c选项:生成目标文件

    root 64 bogon demo ls demo c root 64 bogon demo cat demo c include lt stdio h gt int main puts 34 Hello World 34 return
  • GCC -l选项:手动添加链接库

    标准库的大部分函数通常放在文件 libc a 中 xff08 文件名后缀 a代表 achieve xff0c 译为 获取 xff09 xff0c 或者放在用于共享的动态链接文件 libc so 中 xff08 文件名后缀 so代表 shar
  • GCC 编译使用动态链接库和静态链接库

    1 库的分类 根据链接时期的不同 xff0c 库又有静态库和动态库之分 静态库是在链接阶段被链接的 xff08 好像是废话 xff0c 但事实就是这样 xff09 xff0c 所以生成的可执行文件就不受库的影响了 xff0c 即使库被删除了
  • python爬虫——爬取数据导入excel表

    1 导入第三方库 requests库 re html xlwt span class token keyword from span bs4 span class token keyword import span BeautifulSou
  • Makefile call函数

    引用变量的格式为 变量名 xff0c 函数调用的格式如下 xff1a lt function gt lt arguments gt 或者是 lt function gt lt arguments gt 其中 xff0c function 是
  • Glide生命周期绑定

    Glide class和RequestManagerRetriever class xff0c 主要用来获得RequestManager with返回一个RequestManager public static RequestManager
  • Glide缓存机制

    Glide中采用计数的方式统计资源的引用 xff0c 在每个EngineResource内部都设置一个引用计数acquired xff0c 在加载资源时引用 43 43 xff0c 释放资源时引用 xff1a class EngineRes
  • UML类图

    类图 xff08 Class Diagrams xff09 xff1a 用户根据用例图抽象成类 xff0c 描述类的内部结构和类与类之间的关系 xff0c 是一种静态结构图 在UML类图中 xff0c 常见的有以下几种关系 泛化 xff08
  • android源码github

    https github com aosp mirror platform frameworks base
  • jar 包转 java

    jd gui 内 File gt Save All Sources 直接保存到本地
  • DataBinding源码解析

    DataBinding是Google发布的支持库 xff0c 它可以实现UI组件及数据源的双向绑定 使用DataBinding可以轻松实现MVVM模式 xff0c 当数据发生变化时会体现在View界面上 xff0c 反过来界面内容变化也会同
  • LiveData源码分析

    首先还是以一个示例开始 xff1a MutableLiveData lt String gt liveString 61 new MutableLiveData lt gt liveString observe mOwner new Obs
  • ViewModel源码分析

    首先 xff0c 还是先看一个例子 xff1a public class MyViewModel extends ViewModel private MutableLiveData lt List lt User gt gt users p