Android ViewModel详解

文章目录
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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值