Kotlin
协程基础及原理系列
- 史上最详Android版kotlin协程入门进阶实战(一) -> kotlin协程的基础用法
- 史上最详Android版kotlin协程入门进阶实战(二) -> kotlin协程的关键知识点初步讲解
- 史上最详Android版kotlin协程入门进阶实战(三) -> kotlin协程的异常处理
- 史上最详Android版kotlin协程入门进阶实战(四) -> 使用kotlin协程开发Android的应用
- 史上最详Android版kotlin协程入门进阶实战(五) -> kotlin协程的网络请求封装
Flow
系列
扩展系列
笔者也只是一个普普通通的开发者,设计不一定合理,大家可以自行吸收文章精华,去糟粕。
ViewModel基本使用
在上一篇文中《封装DataBinding让你少写万行代码》中我们已经学会了如何封装DataBinding进行代码优化。本章节我们将在上一篇文章的基础上讲解对ViewModel
的使用封装。
ViewModel基本使用
我们都知道基础的创建ViewModel
方式是通过下面三种方式进行创建的:
1 | KOTLIN复制代码class MainActivity05:AppCompatActivity() { |
因为我们只是为了演示所以MainViewModel
我们只是简单创建了一个mUser: LiveData<User>
对象供于MainActivity
进行observe
。
在上面基础上我们通过引入lifecycle-extensions
这个扩展库。
1 | KOTLIN复制代码 implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' |
我们通过它提供的方法ViewModelProviders
这种方式创建ViewModel
:
1 | KOTLIN复制代码class MainActivity:AppCompatActivity() { |
这种通过ViewModelProviders
这种方式创建ViewModel
,是基于lifecycle-extensions
这个扩展库提供的方法的,我们可以看到实际上ViewModelProviders
最终也是通过ViewModelProvider
去创建。
1 | KOTLIN复制代码/** |
这个方法在早期的版本中是使用到最多的,目前网上很多文章也还是使用的这种写法。但是我们可以看到在高版本的lifecycle-extensions
库中ViewModelProviders
已经被废弃。
通过KTX扩展库方式创建ViewModel
由于ViewModelProviders
已经被废弃了,同时上面的写法确实也稍微有点繁琐。所以为了进一步简化,我们使用到下面ktx库中的方法:
1 | Groovy复制代码 implementation "androidx.activity:activity-ktx:1.2.2" |
我们通过这2个扩展库,使用viewModels
就可以非常快速的创建出我们所需要的ViewModel
,
1 | KOTLIN复制代码class MainActivity:AppCompatActivity() { |
而在Fragment
中我们可以使用activityViewModels
或者viewModels
来创建。
1 | KOTLIN复制代码class HomeFragment:Fragment() { |
可以看到viewModels
其实是ComponentActivity
的一个内联函数,通过在viewModels
函数中调用ViewModelLazy
来获取指定的ViewModel
,同时viewModels
可以传入用于创建ViewModel
的工厂factoryProducer
:
1 | KOTLIN复制代码public inline fun <reified VM : ViewModel> ComponentActivity.viewModels( |
我们通过观察ViewModelLazy
实现可以看到,ViewModelLazy
最终还是调用的ViewModelProvider
方法去创建,这就是传说中的kotlin语法糖
,好甜。
1 | KOTLIN复制代码public class ViewModelLazy<VM : ViewModel> ( |
这里需要提一下的是在Fragment
中通过activityViewModels
创建的ViewModel
,得到的将会这个Fragment
所在的Acivity
的ViewModel
,后面的步骤流程基本就是都是一样的。
1 | KOTLIN复制代码public inline fun <reified VM : ViewModel> Fragment.viewModels( |
可以看到在activityViewModels
方法中实际是调用了requireActivity
来进行创建。如果我们需要实现activity
和Fragment
的数据共享就可以通过这种方式去创建。
通过反射创建ViewModel
虽然经过上面引入KTX库来处理,可能还是不能满足某些人的胃口。毕竟实际开发环境往往存在各种各样的问题,导致不符合实际的开发使用。没关系,我们还可以通过反射的方式再进一步的去处理。
通过上面演进,我们知道不管外部初始化如何变化,最终的方式还是调用ViewModelProvider去创建。这时我们可以参考封装DataBinding的方式,通过反射去创建我们的ViewModel
。
我们先增加一个ComponentActivity
的扩展方法,通过它去获取我们在BaseActivity
中得到泛型ViewModel
的具体类型:
1 | KOTLIN复制代码inline fun <VM: ViewModel> ComponentActivity.createViewModel(position:Int): VM { |
然后我们就可以在BaseActivity
中通过createViewModel
创建我们需要的ViewModel
,这里的BaseActivity
中的ViewDataBinding
不明白的可以去看看封装DataBinding。
1 | KOTLIN复制代码abstract class BaseActivity<VB : ViewDataBinding,VM: ViewModel> : AppCompatActivity(), BaseBinding<VB> { |
修改一下我们的MainActivity
:
1 | KOTLIN复制代码class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() { |
此时我们成功创建了MainViewModel
,通过这种方式节省我们编写大量ViewModel
模板代码的时间。无形中有增加了摸鱼吹水的时间。
但是这个还有个问题,上面的方式只能支持没有参数的ViewModel
。如果需要创建的ViewModel
需要传入参数怎么办。比如我们将MainViewModel
修改为需要一个MainRepository
:
1 | kotlin复制代码class MainViewModel(private val repository: MainRepository):ViewModel() { |
这个时候如果我们还是使用上面的方式,那我们的程序将会崩溃报错,提示无法创建实例化对象:
1 | KOTLIN复制代码java.lang.RuntimeException: Cannot create an instance of class com.carman.kotlin.coroutine.request.viewmodel.MainViewModel |
这个时候我们需要创建一个MainViewModelFactory
来创建我们的MainViewModel
:
1 | KOTLIN复制代码class MainViewModelFactory( |
这个时候我们又该如何把MainViewModelFactory
和上面的反射方法结合到一起使用呢。虽然我在使用ViewModelProvider
可以传入一个Factory
,如我们在最开始MainActivity
直接创建使用的时候:
1 | KOTLIN复制代码ViewModelProvider(this,MainViewModelFactory(MainRepository())).get(MainViewModel::class.java) |
但是我们现在是通过反射的方式去创建,当我们有很多ViewModel
的时候,我们是不知道具体使用哪一个去创建。所以这个时候我们需要创建一个ViewModelUtils
的工具类,然后提取扩展函数createViewModel
方法,我们进一步修改,增加一个factory
参数,通过factory
去判断是否需要使用工厂创建:
1 | KOTLIN复制代码object ViewModelUtils { |
我们再修改一下BaseActivity
在构造方法中增加一个factory
参数。把我们之前直接调用createViewModel
扩展函数修改为使用ViewModelUtils
的createViewModel
方法:
1 | KOTLIN复制代码abstract class BaseActivity<VB : ViewDataBinding, VM : ViewModel>( |
这个时候我们再修改一下MainActivity
,我们使用provideMainViewModelFactory
方法用于获取MainViewModel
的建造工厂MainViewModelFactory
:
1 | KOTLIN复制代码class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>(provideMainViewModelFactory()) { |
这个时候我们再运行我们的程序,一切恢复正常运行。这里我们已经对Activity
中创建ViewModel
进行处理了,那我们在Fragment
中又改如何创建呢。
这个时候我们就需要参考上面Fragment-ktx
中的activityViewModels
和viewModels
方法,也将Fragment
中创建ViewModel
分为2种形式,同时也区分是否需要通过factory
工厂去获取实例化对象:
1 | KOTLIN复制代码fun <VM : ViewModel> createViewModel( |
现在我们再修改一下BaseFragment
,与在Activity
不同的是,由于Fragment
是存在选择与Activity
共享一个ViewModel
,或者自己创建一个私有ViewModel
。所以我们增加了一个shareViewModel
参数来控制我们选择使用哪一种方式创建:
1 | KOTLIN复制代码abstract class BaseFragment<VB : ViewDataBinding, VM : ViewModel>( |
到现在为止,ViewModel
在开发中常用的三种封装使用方式就讲完了。我们接下来讲一讲通过注解的方式去创建,可能有些小伙伴使用过Dagger
,但是我们这里要说的不是使用Dagger
,而是基于Dagger
封装专门为Android环境下的使用的Android Jetpack
组件之一Hilt
。
通过Hilt
注解创建ViewModel
我们先引入Hilt
依赖:
1 | KOTLIN复制代码 classpath 'com.google.dagger:hilt-android-gradle-plugin:2.35.1' |
1 | kotlin复制代码plugins { |
1 | KOTLIN复制代码 implementation "com.google.dagger:hilt-android:2.35.1" |
笔者这里只是作为扩展阅读,不是详细讲解Hilt
如何使用,如果想了解详细使用可以去官网看看使用 Hilt 实现依赖项注入或者有兴趣的可以在地下留言,笔者到时候再找个时间单独写一篇Hil的入门何使用t
。
我们创建一个DemoApplication
不做任何实现,只是增加一个@HiltAndroidApp
注解,它会触发Hilt
的代码生成操作,生成的代码包括应用的一个基类,该基类充当应用级依赖项容器。
1 | KOTLIN复制代码@HiltAndroidApp |
然后在我们需要使用@HiltViewModel
对的MainViewModel
进行注解,同时需要使用@Inject
来执行字段注入
1 | KOTLIN复制代码@HiltViewModel |
同时我们也需要再MainRepository
中使用@Inject
注解进行字段注入:
1 | KOTLIN复制代码class MainRepository @Inject constructor():BaseRepository(){ |
然后在我们的MainActivity
中使用AndroidEntryPoint
注解。@AndroidEntryPoint
为项目中的每个Android 类生成一个单独的 Hilt 组件。这些组件可以从它们各自的父类接收依赖项
1 | KOTLIN复制代码@AndroidEntryPoint |
我们这里是通过与KTX扩展库结合使用,虽然创建MainViewModel
的时候需要传入构建工厂,但是我们通过使用Hilt
注解来帮助我们注入。
需要源码的看这里:demo源码
原创不易。如果您喜欢这篇文章,您可以动动小手点赞收藏。
关联文章
Kotlin
协程基础及原理系列
- 史上最详Android版kotlin协程入门进阶实战(一) -> kotlin协程的基础用法
- 史上最详Android版kotlin协程入门进阶实战(二) -> kotlin协程的关键知识点初步讲解
- 史上最详Android版kotlin协程入门进阶实战(三) -> kotlin协程的异常处理
- 史上最详Android版kotlin协程入门进阶实战(四) -> 使用kotlin协程开发Android的应用
- 史上最详Android版kotlin协程入门进阶实战(五) -> kotlin协程的网络请求封装
Flow
系列
扩展系列
本文转载自: 掘金