Android ViewModel详解

2023-11-15

文章目录
ViewModel简介
ViewModel生命周期
ViewModel 使用
Fragment使用
ktx 扩展
activity 扩展
fragment 扩展
AndroidViewModel
ViewModel onCleared 原理解析
Activity 中的 ViewModel 原理
Fragment 中的 ViewModel 原理
因为 Activity 销毁导致 Fragment 销毁
因为 Fragment 替换导致 Fragment 销毁
ViewModel简介
视图与数据模型之间的桥梁ViewModel

ViewModel生命周期

ViewModel的生命周期会比创建它的Activity、Fragment的生命周期都要长。即ViewModel中的数据会一直存活在Activity/Fragment中。

众所周知,由于Android平台的特殊性,若应用程序发送屏幕旋转的时候会经历Activity的销毁与重建,这里就涉及到数据保存的问题。虽然Activity可以通过onSaveInstanceState()机制保存与恢复数据,但是onSaveInstanceState()方法只能存储少量的数据进行恢复,但是遇到大量的数据该怎么办呢?

幸运的是,ViewModel能完美的为我们解决这个问题,ViewModel有自己独立的生命周期,屏幕旋转所导致的Activity重建,并不会影响ViewModel的生命周期.

ViewModel 使用
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
1
写一个继承自ViewModel的类

class MyViewModel : ViewModel() {
    private val users: MutableLiveData<List<User>> by lazy {
        MutableLiveData<List<User>>().also {
            loadUsers()
        }
    }

    fun getUsers(): LiveData<List<User>> {
        return users
    }

    private fun loadUsers() {
        // Do an asynchronous operation to fetch users.
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
使用:

class MainActivity : AppCompatActivity() {

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

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

        //或者引入 extensions
        //implementation "android.arch.lifecycle:extensions:1.1.1"
        val mainViewModel = ViewModelProviders.of(this).get(MyViewModel::class.java);
    }

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ViewModel是一个抽象类,其中只有一个onCleared()方法。当ViewModel不再被需要,即与之相关的Activity都被销毁时,该方法会被系统调用。我们可以在该方法中执行一些资源释放的相关操作。注意: 当屏幕旋转而导致的Activity重建,并不会调用该方法。

class MyViewModel : ViewModel() {

    override fun onCleared() {
        super.onCleared()
        //viewModel销毁时调用,可以做一些释放资源的操作
    }
}
1
2
3
4
5
6
7
我们可以在onCleared()对定时器资源的释放,防止造成内存泄露。

Fragment使用
class ItemFragment : Fragment() {

    var mainViewModel: MyViewModel? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val mainViewModel = ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_item_list, container, false)
        return view
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ktx 扩展
activity 扩展
在上面我们介绍了,在activity里获取 viewModel 实例的方法,如下:

val mainViewModel = ViewModelProvider(this).get(MyViewModel::class.java)
1
但是 Kotlin 的 ktx 扩展包里面有更为简洁的方式,添加依赖如下:

implementation "androidx.activity:activity-ktx:1.3.1"
1
使用如下:

class MainActivity : AppCompatActivity() {

    val mainViewModel by viewModels<MyViewModel>()

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

        mainViewModel.name = "hhhh"
    }
}
1
2
3
4
5
6
7
8
9
10
11
viewModels 扩展方法如下:

public inline fun <reified VM : ViewModel> ComponentActivity.viewModels(
    noinline factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
    val factoryPromise = factoryProducer ?: {
        defaultViewModelProviderFactory
    }

    return ViewModelLazy(VM::class, { viewModelStore }, factoryPromise)
}
1
2
3
4
5
6
7
8
9
fragment 扩展
对于 fragment 我们以前是使用:

val mainViewModel = ViewModelProvider(requireActivity()).get(MyViewModel::class.java)
1
同理,我们添加 fragment ktx 扩展

implementation "androidx.fragment:fragment-ktx:1.3.6"
1
就可以使用 :

val mainViewModel: MyViewModel by activityViewModels()
1
具体代码如下

class ItemFragment : Fragment() {

    val mainViewModel: MyViewModel by activityViewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        val view = inflater.inflate(R.layout.fragment_item_list, container, false)
        return view
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
AndroidViewModel
使用ViewModel的时候,需要注意的是ViewModel不能够持有View、Lifecycle、Acitivity引用,而且不能够包含任何包含前面内容的类。因为这样很有可能会造成内存泄漏。

普通的 ViewModel 生命周期都很短,随着activity 销毁而销毁。如果我们要创建一个长生命周期的 ViewModel 怎么办? 其实Android 已经给我们提供了一个 AndroidViewModel

下面是一个AndroidViewModel的源码:

public class AndroidViewModel extends ViewModel {
    @SuppressLint("StaticFieldLeak")
    private Application mApplication;

    public AndroidViewModel(@NonNull Application application) {
        mApplication = application;
    }

    /**
     * Return the application.
     */
    @SuppressWarnings({"TypeParameterUnusedInFormals", "unchecked"})
    @NonNull
    public <T extends Application> T getApplication() {
        return (T) mApplication;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
可以看到 AndroidViewModel 持有了一个 Application ,所以它的生命周期会很长。

具体使用如下:

class MyViewModel(application: Application) : AndroidViewModel(application) {

    override fun onCleared() {
        super.onCleared()
        //viewModel销毁时调用,可以做一些释放资源的操作
    }

}
1
2
3
4
5
6
7
8
其实延伸开,我们完全可以在 AndroidViewModel 中存储一些全局数据。

ViewModel onCleared 原理解析
你有没有想过,当 Activity 、Fragment 销毁的时候,ViewModel 的 onCleared 方法为什么会回调?

当你看到下面这两个图,你就明白了

Activity 中的 ViewModel 原理
ComponentActivity 注册 LifecycleEventObserver , 在 onDestory() 执行 getViewModelStore().clear();

ViewModelStore

上面就是 Activity 的原理。

Fragment 中的 ViewModel 原理
因为 Activity 销毁导致 Fragment 销毁
对于 Fragment 来说,要复杂一下:

FragmentActivity


FragmentController

FragmentStateManager

FragmentManagerViewModel

因为 Fragment 替换导致 Fragment 销毁
比如在 Activity 先执行 , add 方法

      supportFragmentManager.beginTransaction()
                .add(R.id.fragc, MyFragmentA(), "sdsds")
                .commitAllowingStateLoss()
1
2
3
再执行 replace 方法

    supportFragmentManager.beginTransaction()
            .replace(R.id.fragc, MyFragmentB(), "sdsds")
            .commitAllowingStateLoss()
1
2
3
执行完 replace MyFragmentB , 那么 MyFragmentA 就会被销毁。对应的 MyFragmentA 中的 ViewModel 就会执行 onCleared 方法。

原理如下:

FragmentStateManager

FragmentManagerViewModel

这种情况和上一种不一样 。

本次情况 Activity 没销毁,是因为 fragment 执行 replace 导致前 fragment 销毁。
而上一种情况是因为 Activity 销毁,导致 fragment 销毁。
————————————————
版权声明:本文为CSDN博主「赵彦军」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/zhaoyanjun6/article/details/119828016

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

Android ViewModel详解 的相关文章

  • 如何在用户输入数据后重新运行java代码

    嘿 我有一个基本的java 应用程序 显示人们是成年人还是青少年等 我从java开始 在用户输入年龄和字符串后我找不到如何制作它它们被归类为 我希望它重新运行整个过程 以便其他人可以尝试 的节目 我一直在考虑做一个循环 但这对我来说没有用
  • Java ResultSet 如何检查是否有结果

    结果集 http java sun com j2se 1 4 2 docs api java sql ResultSet html没有 hasNext 方法 我想检查 resultSet 是否有任何值 这是正确的方法吗 if resultS
  • tomcat 中受密码保护的应用程序

    我正在使用 JSP Servlet 开发一个Web应用程序 并且我使用了Tomcat 7 0 33 as a web container 所以我的要求是tomcat中的每个应用程序都会password像受保护的manager applica
  • 如何对不同的参数类型使用相同的java方法?

    我的问题 我有 2 个已定义的记录 创建对象请求 更新对象请求 必须通过实用方法进行验证 由于这两个对象具有相同的字段 因此可以对这两种类型应用相同的验证方法 现在我只是使用两种方法进行重载 但它很冗长 public record Crea
  • 使用片段时应用程序崩溃

    我正在处理碎片和 我的代码中有一个我找不到的问题 logcat 指向我的一个片段中的这段代码 Override public View onCreateView LayoutInflater inflater ViewGroup conta
  • 在我的 Spring Boot 示例中无法打开版本 3 中的 Swagger UI

    我在 Spring Boot 示例中打开 swagger ui 时遇到问题 当我访问 localhost 8080 swagger ui 或 localhost 8080 root api name swagger ui 时出现这种错误 S
  • Android 中如何通过彩信发送图片?

    我正在开发多媒体应用程序 我正在通过相机捕获一张图像 并希望将该图像和文本发送到其他号码 但我不知道如何通过彩信发送图像 MMS 只是一个 http post 请求 您应该使用执行请求额外的网络功能 final ConnectivityMa
  • java.io.Serialized 在 C/C++ 中的等价物是什么?

    C C 的等价物是什么java io Serialized https docs oracle com javase 7 docs api java io Serializable html 有对序列化库的引用 用 C 序列化数据结构 ht
  • Android 中的处理程序与异步调用

    目前我正在使用处理程序来调用 Web 服务方法以使其在后台运行 问题是它需要更多的时间来给出响应 在性能方面似乎更昂贵 现在我计划使用异步调用 哪一个是最好的 Android 中的处理程序和异步调用有什么区别 请帮我想出一个最好的解决方案
  • 卡片视图 单击卡片移至新活动

    我是 Android 编程新手 正在研究卡片布局 我想知道如何使其可点击 android clickable true android foreground android attr selectableItemBackground 我的卡
  • 在命令行上卸载 Android SDK 的选定部分

    这与 卸载旧的 Android SDK 版本 https stackoverflow com questions 15182377 uninstall old android sdk versions 除非我想在无头 Linux CI 服务
  • 通过系统应用程序以编程方式静默安装 apk(无需 root)

    我有带有 android sharedUserId android uid system UID 1000 的系统级应用程序 设备未root INSTALL PACKAGES 权限包含在清单中 我可以静默安装下载的 apk 吗 我已经发现这
  • 插件“Android Bundle Support”不兼容

    大家好 自从上次更新以来 当我启动 android studio 时 我遇到了一个非常奇怪的错误 我有这个错误 插件错误 插件 Android Bundle Support 不兼容 直到构建 AI 195 SNAPSHOT 我在网上找不到任
  • 问题:为什么React Native Video不能全屏播放视频?

    我正在react native 0 57 7 中为android和ios创建一个应用程序并使用反应本机视频 https github com react native community react native video播放上传到的视频
  • 长轮询会冻结浏览器并阻止其他 ajax 请求

    我正在尝试在我的中实现长轮询Spring MVC Web 应用程序 http static springsource org spring docs 2 0 x reference mvc html但在 4 5 个连续 AJAX 请求后它会
  • 如何将双精度/浮点四舍五入为二进制精度?

    我正在编写对浮点数执行计算的代码的测试 不出所料 结果很少是准确的 我想在计算结果和预期结果之间设置一个容差 我已经证实 在实践中 使用双精度 在对最后两位有效小数进行四舍五入后 结果始终是正确的 但是usually四舍五入最后一位小数后
  • 使用 svn 1.8.x、subclise 1.10 的 m2e-subclipse 连接器在哪里?

    我读到 m2e 的生产商已经停止生产 svn 1 7 以外的任何版本的 m2e 连接器 Tigris 显然已经填补了维护 m2e subclipse 连接器的空缺 Q1 我的问题是 使用 svn 1 8 x 的 eclipse 更新 url
  • 如果没有抽象成员,基类是否应该标记为抽象?

    如果一个类没有抽象成员 可以将其标记为抽象吗 即使没有实际理由直接实例化它 除了单元测试 是的 将不应该实例化的基类显式标记为抽象是合理且有益的 即使在没有抽象方法的情况下也是如此 它强制执行通用准则来使非叶类抽象 它阻止其他程序员创建该类
  • 双枢轴快速排序和快速排序有什么区别?

    我以前从未见过双枢轴快速排序 是快速排序的升级版吗 双枢轴快速排序和快速排序有什么区别 我在 Java 文档中找到了这个 排序算法是双枢轴快速排序 作者 弗拉基米尔 雅罗斯拉夫斯基 乔恩 本特利和约书亚 布洛赫 这个算法 在许多数据集上提供
  • Spring Rest 和 Jsonp

    我正在尝试让我的 Spring Rest 控制器返回jsonp但我没有快乐 如果我想返回 json 但我有返回的要求 完全相同的代码可以正常工作jsonp我添加了一个转换器 我在网上找到了用于执行 jsonp 转换的源代码 我正在使用 Sp

随机推荐

  • 在Java中response如何设置文件路径

    在 Java 中 使用 java io File 类来设置文件路径 例如 下面的代码展示了如何创建一个 File 对象 并使用它来设置文件路径 import java io File 创建一个 File 对象 表示当前目录下的 test t
  • “warning NU1701: 项目依赖包与项目框架net6.0不完全兼容“

    问题 一个Net6的Web项目 有一个警告 warning NU1701 已使用 NETFramework Version v4 6 1 NETFramework Version v4 6 2 NETFramework Version v4
  • 服务器之间如何传输数据

    有时候大家需要将一台服务器内的数据传输到另外一台设备 有很多种方法 如果两台物理机离得很近 可以通过移动硬盘传输 如果是跨地区的 有人首先通过filezilla将数据下载到windows电脑上 在通过filezilla上传到另外一台服务器
  • 小红书怎么做关键词搜索排名?哪些行业适合在小红书推广?

    小红书 是口碑营销 社群营销 笔记营销 是大众点评的电商版本 靠分享打天下的 分享就是口碑 很多人问小红书怎么做关键词搜索排名 今天就小编带你领略一下小红书的关键词排名引流秘籍 一 搞关键词布局 1 文章标题中必须要带有关键词 醒目的标题
  • LeetCode-1343. Maximum Product of Splitted Binary Tree

    Given a binary tree root Split the binary tree into two subtrees by removing 1 edge such that the product of the sums of
  • 解决windows环境下cmake无法生成Makefiles文件

    Step1 首先确保你的电脑安装了make 如何安装了MinGW可以将bin目录下的mingw32 make exe或mingw64 make exeq强制改为make exe并添加环境变量也可以 Step2 执行代码 cmake G Un
  • HTTP API网关选择之一Kong介绍

    Kong是Mashape开源的高性能高可用API网关和API服务管理层 它基于OpenResty 进行API管理 并提供了插件实现API的AOP Kong在Mashape 管理了超过15 000 个API 为200 000开发者提供了每月数
  • 四. Gateway 限流

    目录 一 限流中的基础问题 1 为什么限流及常见限流方案 2 常见限流算法 计数器限流算法 令牌桶算法 漏桶算法 3 几种基础版限流实现方案 基于redis实现限流 基于 Guava RateLimiter 实现令牌算法 二 Gateway
  • 详解POW工作量证明原理

    原文地址 来自 微信公众号 区块链大师 POW工作量证明 英文全称为Proof of Work 早在比特币出现之前就已经有人探索 常见的是利用HASH运算的复杂度进行CPU运算实现工作量确定 当然你也可以利用卷积求导 大质数分解这些复杂的运
  • 14-7 使用 css 调控样式

    1 修改前端样式 可以将 ui 文件与 css 文件进行关联 类似于 html 和 css 的关系 只不过需要注意的是 前端样式可以借助 glade 进行修改 但并没有将修改应用至程序窗口 仅仅提供了预览功能 样式修改后还需要修改后台代码应
  • DeepLabv3+

    DeepLabv3 引言 语义分割中的DCNN主要有两种结构 空间金字塔池化SPP和编码器 解码器encoder decoder SPP通过多种感受野池化不同分辨率的特征来挖掘上下文信息 Encoder decoder逐步重构空间信息来更好
  • chatGTP给出的python版快速排序

    chatGTP给出的python版快速排序 def quick sort arr 快速排序函数 对给定的数组 arr 进行原地排序 param arr 待排数组 return 原地排序后的数组 arr quick sort helper a
  • MediaWiki的安装与使用

    文章目录 操作环境与相关配置信息 操作环境 配置信息 软件安装 运行MediaWiki容器 运行MySQL 使用MediaWiki连接MySQL 使用docker compose 操作环境与相关配置信息 操作环境 操作系统 root mas
  • 数据库安装+OGG数据同步

    数据库 Oracle 11g 操作系统 linux 7 2 数据库安装 单机 gt ogg搭建 gt 数据同步 一 数据库安装 查看操作系统版本 oracle standbbk cat etc redhat release Red Hat
  • 巧用快捷方式实现自定义Win+R命令(VBS版).

    另一篇文章 巧用快捷方式实现自定义Win R命令 手动版 介绍了如何手动创建自定义Win R命令 这篇文章将介绍如何使用脚本自动创建并管理自定义Win R命令 为什么要使用这个脚本 大家都知道用Win R能输入很多windows内置命令 比
  • Qt Table 的表头合并

    最近接到了开发表格的需求 一般来说网上有很多不错的例子 表格的开发多半使用QtableWeiget或者Qtableview 为了美观 有很多样式设置的代码行 基本外观设置 FriendTable gt setFrameShape QFram
  • 蓝桥杯历届试题-小朋友排队

    题目 题目链接 题解 树状数组求逆序对 好早之前写过逆序对的三种求法 看明白了树状数组求逆序对的方法后本题就很轻松了 本题思路 高矮不满足要求的相邻两个小朋友要互换位置 且二者的不高兴程度都是增加 所以对于某个小朋友而言 其左侧的高个会与其
  • 题目:输入一个链表的头结点,从尾到头反过来输出每个结点的值

    题目 输入一个链表的头结点 从尾到头反过来输出每个结点的值 链表结点定义如下 struct ListNode int m nKey ListNode m pNext 分析 这是一道很有意思的面试题 该题以及它的变体经常出现在各大公司的面试
  • 面试 算法题 在字符串中寻找最长的数字串(包含+-)

    在字符串中找到最长的数字串 包含 1 数字开头可以有 号 2 数字可以有小数点 3 小数点前后必须要有数字 4 有相同长度的取靠后的一串 返回结果串 没有结果返回空串 面试的时候没多想 直接一个窗口往字符串上撸 情况太复杂了撸挂了 时间不多
  • Android ViewModel详解

    文章目录 ViewModel简介 ViewModel生命周期 ViewModel 使用 Fragment使用 ktx 扩展 activity 扩展 fragment 扩展 AndroidViewModel ViewModel onClear