ViewModel

ViewModel 简介

我们这里说的ViewModel 是说的是jetpack提供的一个组件库,他跟mvvm里说的VM层是两个不同的概率,不要混淆。
只不过VM层会 只用ViewModel这个组件,ViewModel的作用就是为了数据的稳定性。

ViewModel的基本使用

ViewModel的作用就是在activity的生命周期 在还没走ondestory之前ViewModel的数据都不会丢失,也就是不会清除
但是走了onDestory 也不一定会被清除,下面再原理的时候会解析
当清除的这个时候 ViewModel 会调用 onCleared 的方法,最终把 Viewmodel里面的数据清空

当然,当你需要在一定的逻辑场景的时候想要清除掉ViewMode里面的所有属性数据, 可人为调用 这个onCleared 方法

使用如下

自定义一个类继承 ViewModel 里面存在着数据

class ViewModelData : ViewModel() {

    var  count : Int = 0

    //在一些场景中, 可以重写这个方法,在这个方法让 当前正在加载的业务,取消加载  做到,页面销毁, ViewModel 的数据清空掉 ,数据加载也自动取消的效果
    override fun onCleared() {
        super.onCleared()
    }
}

使用 ViewModel如下

class MainActivity : AppCompatActivity() {

    private lateinit var viewModelData: ViewModelData

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

        setContentView(R.layout.activity_main2)

        //viewModel 实例化需要用此写法 而不是new
        viewModelData = ViewModelProvider(this).get(ViewModelData::class.java)

  
    }

}

上面的是老版本的获取方式,如果你添加了如下的ktx 依赖

    implementation "androidx.activity:activity-ktx:1.5.1"
    implementation "androidx.fragment:fragment-ktx:1.5.2"

那么可以用以下的获取方式;

  val viewmodel : MainViewModule  by viewModels()//这个是注入获取的方式  但是需要添加依赖

Activity与Fragment共享一个ViewModel

如果你想在 Fragment 中使用 Activity 的 ViewModel,通常意味着你希望共享同一个 ViewModel 实例,而 ViewModel 内存有着数据变量,所以 共享同一个 ViewModel 实例 以便在 Fragment 和 Activity 之间共享数据

class MyFragment : Fragment() {  
    private val viewModel: MyViewModel by viewModels {  
        requireActivity().createViewModelProvider(MyViewModel::class.java)  
    }  
  
    override fun onCreateView(  
        inflater: LayoutInflater, container: ViewGroup?,  
        savedInstanceState: Bundle?  
    ): View? {  
        // Inflate the layout for this fragment  
        return inflater.inflate(R.layout.fragment_my, container, false)  
    }  
  
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {  
        super.onViewCreated(view, savedInstanceState)  
  
    }  
}

ViewModel 的注意事项

为什么 ViewModel 容易引起内容泄漏?

ViewModel 的生命周期比 Activity 或 Fragment 的生命周期要长,当旋转屏幕的时候, Activity 或 Fragment 会被销毁并且重新创建,但是 ViewModel 还是不变,

也就是说当 Activity 或 Fragment 要被销毁的时候,因为 ViewModel 还在持有 Activity 或 Fragment ,导致 Activity 或 Fragment没有办法被gc 回收,所以导致内存泄漏

容易引起泄漏的操作:

错误的依赖注入:如果你将 ViewModel 与依赖注入库(如 Dagger、Hilt)一起使用,并错误地将 Activity 或 Fragment 的上下文(如 Context 或 LifecycleOwner)注入到 ViewModel 中,那么当 Activity 或 Fragment 被销毁时,这些上下文仍然会被 ViewModel 持有,导致内存泄漏。

持有非静态内部类实例:我们说非静态内部类默认持有外部类的引用,那么如果ViewModel 持有 Activity 或 Fragment 的非静态内部类实例,而因为 这些 非静态内部类实例
有Activity 或 Fragment 引用,那么这也可能导致内存泄漏。

监听器和回调:当 ViewModel 注册为某些外部资源的监听器或回调时(如数据库、网络请求等),如果这些资源在 ViewModel 仍然存在时保持对 ViewModel 的引用,并且 ViewModel 又持有对 Activity 或 Fragment 的引用,那么这也可能导致内存泄漏。

其实总结一句话就是, ViewModel里面的对象引用不要直接或者间接持有 Activity 或 Fragment 的引用

正因为如此,ViewModel绝对不要持有下列引用
1.view 2.Lifecycle 3.其他任何可能持有Activity Context的类的引用

如果要用Context,也是用Application 这个context,因为他的生命周期是整个应用进程,写法就是继承 AndroidViewModel

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

    private val  mApplication = application

}

注意用非activity的context startActivity 内设置一个Flag NewTask 不然会崩溃

ViewModel 原理:

看下ViewModel的创建方式

var viewModel = ViewModelProvider(this,ViewModerProvider.NewInstanceFactory()).get(ViewModelData::class.java)

第一个参数就是叫做 ViewModelStoreOwner,而这里传入的this,也就是把activity给传进去,
正是因为activity 也是 ViewModerStoreOwner

在这里插入图片描述
而每个ViewModelStoreOwner又有一个 ViewModelStore

在这里插入图片描述
ViewModelStore 里面只是个HasMap ,根据key,存放着ViewModel

在这里插入图片描述
那么也就是说 activity 就是 ViewModerStoreOwner,里面有ViewModelStore 实例对象 ,ViewModelStore 有个HashMap里面根据key存放着各种ViewModel

而activity 又是LifecycleOwner,通过Lifecycle,设置一个观察者,LifecycleEventObserver
当一定条件的时候,就会获取 activity 的 ViewModelStore 实例对象 ,调用 HashMap中的所有ViewModel执行onclear 清除数据操作

这条件就是
收到的生命周期状态是 onDestroy也就是的Event是Lifecycle.Event.ON_DESTROY的时候做判断
并且 isChangingConfigurations是false 才会调用 ViewModel执行onclear

前面说了ViewModel在ondestory之前数据不会清空,但是就算走了onDestoy的时候也不一定会情况就是这里的判断

在这里插入图片描述
这里还多了一层判断,就是 isChangingConfigurations ,也就是看这个 mChangingConfigurations boolean 的情况
意思就是如果配置改变了 mChangingConfigurations = true, 那么就算调用了 ondestory 也不会走到下面clear方法去
清空ViewModel的数据,而 isChangingConfigurations 这个boolean 它默认就是 false,也就是默认情况下, 配置是没有改变的,也就是默认情况下 ondestory 走,数据是会清空的,但是,比如 activity横竖屏切换的时候,它是会走onDestory方法的
但是这个时候 mChangingConfigurations = true 会是true,也就是配置改变了,所以这个时候,就算走了 ondestory 数据是不会被清空的

在这里插入图片描述

在这里插入图片描述
/** true if the activity is being destroyed in order to recreate it with a new configuration */
这段注释的意思是
如果活动被销毁是为了用新的配置重新创建它,那么 mChangingConfigurations 是true,不然是false
好比横竖屏切换的时候就会是true

而当走清空数据方法后,
会调用getViewModelStore这个方法获取ViewModelStore这个实例对象,然后调用clear方法,
把 ViewModelStore 的HashMap的ViewModel调用它的 clear方法去清除数据

在这里插入图片描述

LiveData+Databinding+ViewModel

一般在项目中,要做到数据驱动ui,首先,为了数据不在 ondestory 之前被清除, 数据会放在 ViewModel 类里 ,

接着大多是采用的 利用databinding 的作用,在布局里把界面跟ViewModel 的数据绑定在一起,
(当然也可以 采用 Livedata 去手动设置监听数据界面改变,看情况)

ViewModel 的数据 是可以使用 ObserableFied 也可以使用 MutableLivedata ,但是 数据 用Livedata 的 MutableLivedata ,会更好,

LiveData 作为一种观察者设计模式,具有生命周期感知的特点,可以在 Activity 或 Fragment 生命周期结束时自动解除注册。同时,MutableLiveData 还提供了 setValue() 和 postValue() 方法,可以在主线程或子线程中更新 LiveData 的值,同时保证数据的线程安全。
而 ObserableFied 没有这个功能,不能根据生命周期

不过,如果说你的数据,他是频繁的改变的,比如说是进度条,0到100不断改变,
那么最好还是用 ObserableFied ,防止抖动,节约内存

当 ViewModel 的 LiveData 的数据变化时,会根据Lifecycle 调用的生命周期 在可见的时候 ,LiveData 会通知所有观察者 包括 DataBinding,正是因为观察者里面有 DataBinding 所以 DataBinding 收到数据改变 才会驱动界面去更新数据

所以关键一个点,

Viewmodel 里面放着ObserableFied 数据, ObserableFied 数据改变 , databinding 会 驱动界面也跟着改变

但是 Viewmodel 里面放着MutableLiveData 数据, MutableLiveData 数据改变 , databinding 不会 驱动界面也跟着改变

还要 在 activity 加上一句代码

activityMainBinding.setLifecycleOwner(this)

setLifecycleOwner(this)源码如下

在这里插入图片描述

接着 setLifecycleOwner 会来到这里
在这里插入图片描述
也就是设置 当前的LifecycleOwner 是给到Livedata用的,

因为Livedata 使用它 都需要传个LifecycleOwner过去
目前没有哪句代码有把LifecycleOwner给过去, 单单就只是设置了个vm,vm 里面有个livedata, 界面里面数据绑定了
vm的livedata, activity里面也只是 创建了vm实例 ,设置vm 实例到布局里面

只有 activity 设置了这句话

activityMainBinding.setLifecycleOwner(this)

把 LifecycleOwner 给到livedata, 然后 liveData.observe(LifecycleOwner,this)
当 liveData 的数据改变的时候,会再根据生命周期最后调用 观察者们的 onchange 方法,其中这里的观察者就是 databinding
最后在onChange 方法里面 databinding 去驱动ui

在这里插入图片描述

所以最终就是 LiveData+Databinding+ViewModel

ViewModel

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

    val liveData:MutableLiveData<String> by lazy{
        MutableLiveData<String>()
    }

    override fun onCleared() {
        super.onCleared()
    }
}

MainActivity

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private lateinit var mainViewModel: MainViewModel

    @RequiresApi(Build.VERSION_CODES.O)
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        var activityMainBinding: ActivityMainBinding =
            DataBindingUtil.setContentView(this, R.layout.activity_main)

        mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
            
        activityMainBinding.mainViewModel =mainViewModel
        activityMainBinding.setLifecycleOwner(this)
    }

布局

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="mainViewModel"
            type="com.android.myapplication.MainViewModel" />

    </data>


    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="8dp"
            android:layout_gravity="center"
            android:text="@{mainViewModel.liveData}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:id="@+id/text"/>

    </FrameLayout>
</layout>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
ViewModelAndroid 架构组件之一,旨在帮助解决 Activity 和 Fragment 与数据持久性相关的问题。ViewModel 类的实例会存储和管理与视图相关的数据,这样即使是配置更改(如旋转屏幕)或者内存回收等情况,数据也不会丢失。ViewModel 通过使用“生命周期感知”来实现这一点,即 ViewModel 的生命周期与 Activity 或 Fragment 的生命周期相关联。 在使用 ViewModel 时,通常的做法是创建一个 ViewModel 子类,将需要管理的数据作为类的成员变量,并提供对应的 get/set 方法。Activity 或 Fragment 可以通过获取 ViewModel 实例并调用其方法来访问和修改数据。 以一个简单的计数器为例,以下是一个使用 ViewModel 的代码示例: 1. 创建 ViewModel 子类 ```kotlin class CounterViewModel : ViewModel() { private var count = 0 fun getCount(): Int { return count } fun incrementCount() { count++ } } ``` 2. 在 Activity 或 Fragment 中获取 ViewModel 实例 ```kotlin val viewModel = ViewModelProviders.of(this).get(CounterViewModel::class.java) ``` 3. 在 Activity 或 Fragment 中使用 ViewModel ```kotlin // 获取计数器的值 val count = viewModel.getCount() // 增加计数器的值 viewModel.incrementCount() ``` 需要注意的是,ViewModel 只能存储与视图相关的数据,不能存储与 Android 系统相关的数据,如 Context、View 等。此外,ViewModel 的生命周期与 Activity 或 Fragment 的生命周期相关联,因此在使用 ViewModel 时也需要确保正确处理生命周期。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值