Kotlin基础从入门到进阶系列讲解(进阶篇)Jetpack,(更新中)
Posted 彬sir哥
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin基础从入门到进阶系列讲解(进阶篇)Jetpack,(更新中)相关的知识,希望对你有一定的参考价值。
Kotlin基础从入门到进阶系列讲解(进阶篇)Jetpack
Jetpack
一、Jetpack 简介
Jetpack是一个开发组件工具集,它的主要目的是帮助我们编写出更加简洁的代码,并简化我们的开发过程。Jetpack中的组件有一个特点,它们大部分不依赖于任何android系统版本,这意味着这些组件通常是定义在AndroidX库当中的,并且拥有非常好的向下兼容性
先来看一张Jetpack目前的“全家福”,如下图:
二、ViewModel
ViewModel应该可以算是Jetpack中最重要的组件之一了。其实Android平台上之所以会出现诸如MVP、MVVM之类的项目架构,就是因为在传统的开发模式下,Activity的任务实在是太重了,既要负责逻辑处理,又要控件UI展示,甚至还得处理网络回调,等等。
而ViewModel的一个重要作用就是可以帮助Activity分担一部分工作,它是专门用于存放与界面相关的数据的。也就是说,只要是界面上能看得到的数据,它的相关变量都应该存放在ViewModel中,而不是Activity中,这样可以在一定程度上减少Activity中的逻辑
另外,ViewModel还有一个非常重要的特征。当手机发生横竖屏旋转的时候,Activity会被重新创建,同时存放在Activity中的数据也会丢失。而ViewModel的生命周期和Activity不同,它可以保证在手机屏幕发生旋转的时候不会被重新创建,只有当Activity退出的时候才会跟着Activity一起销毁。因此,将与界面相关的变量存放在ViewModel当中,这样即使旋转手机屏幕,界面上显示的数据也不会丢失。
ViewModel的生命周期如下图:
简单的计数器:ViewModel的基本用法
1、ViewModel的基本用法
如果想要使用ViewModel组件,还需要在app/build.gradle文件中添加依赖包:
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
MainActivity创建一个对应的MainViewModel类,并让它继承自ViewModel,代码:
class MainViewModel:ViewModel()
在ViewModel中加入一个counter变量用于计数,代码:
class MainViewModel:ViewModel()
var counter = 0
现在我们需要在界面上添加一个按钮,每点击一次按钮就让计数器加1,并且把最新的计数显示在界面上。修改activity_main.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/infoText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textSize="30sp" />
<Button
android:id="@+id/plusOneBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Plus One"
android:textAllCaps="false"/>
</LinearLayout>
布局文件非常简单,一个TextView用于显示当前的计数,一个Button用于对计数器加1。
修改MainActivity代码:
class MainActivity : AppCompatActivity()
lateinit var viewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
plusOneBtn.setOnClickListener
viewModel.counter++
refreshCounter()
refreshCounter()
private fun refreshCounter()
infoText.text = viewModel.counter.toString()
第1图:程序的初始化界面,第2图:点击按钮计数器增长,第3图:旋转手机屏幕后,就会发现Activity虽然被重新创建了,但是计数器的数据却没有丢失
2、向ViewModel传递参数
我刚说中创建的 MainViewModel 的构造函数中没有任何参数,但是思考一下,如果我们确实需要通过构造函数来传递一些参数,应该怎么办呢?由于所有 ViewModel 的实例都是通过ViewModelProvider来获取的,因此我们没有任何地方可以向ViewModel的构造函数中传递参数
这个问题也不难解决,只需要借助ViewModelProvider.Factory就可以实现了。下面测试代码:
现在的计数器虽然在屏障旋转的时候不会丢失数据,但是如果退出程序之后再重新打开,那么之前的计数就会被清零了。接下来对这一功能进行升级,保证即使在退出程序后又重新打开的情况下,数据仍然不会丢失
实现这个功能需要在退出程序的时候对当前的计数进行保存,然后在重新打开程序的时候读取之前保存的计数,并传递给 MainViewModel 。因此,这里修改MainViewModel中的代码:
class MainViewModel(countReserved:Int): ViewModel()
var counter = countReserved
给 MainViewModel 的构造函数添加了一个countReserved参量,这个参数用于记录之前保存的计数值,并在初始化的时候值给counter变量
接下来的问题就是如何向MainViewModel的构造函数传递数据了,前面已经说了需要借助ViewModelProvider.Factory,下面我们就来看看具体应该如何实现
新建一个MainViewModelFactory类,并让它实现ViewModelProvider.Factory接口,代码:
class MainViewModelFactory(private val countReserved: Int) : ViewModelProvider.Factory
override fun <T : ViewModel> create(modelClass: Class<T>): T
return MainViewModel(countReserved) as T
可以看到,MainViewModelFactory的构造函数中也接收一个countReserved参数。另外ViewModelProvider.Factory接口要求实现create()方法,因此这里在create()方法中创建了MainViewModel的实例,并将countReserved参数传了进去。为什么这里就可以创建MainViewModel的实例了呢?因为create()方法的执行时机和Activity的生命周期无关,所以不会产生之前提到的问题
在界面上添加一个清零按钮,修改activity_main.xml代码:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
<Button
android:id="@+id/ClearBtn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Clear"
android:textAllCaps="false" />
</LinearLayout>
修改MainActivity代码:
class MainActivity : AppCompatActivity()
lateinit var viewModel: MainViewModel
lateinit var sp: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
sp = getPreferences(Context.MODE_PRIVATE)
val countReserved = sp.getInt("count_reserved", 0)
viewModel = ViewModelProvider(
this,
MainViewModelFactory(countReserved)
).get(MainViewModel::class.java)
plusOneBtn.setOnClickListener
viewModel.counter++
refreshCounter()
refreshCounter()
ClearBtn.setOnClickListener
viewModel.counter = 0
refreshCounter()
refreshCounter()
override fun onPause()
super.onPause()
sp.edit
putInt("count_reserved", viewModel.counter)
private fun refreshCounter()
infoText.text = viewModel.counter.toString()
在onCreate()方法中,首先获取了SharedPreferences的实例,然后读取之前保存的计数值,如果没有读以的话,就使用0作为默认值。接下来在ViewModelProvider中,额外传入了一个MainViewModelFactory参数,这里将读取到的计数值传给了MainViewModelFactory的构造函数。注意,这一步是非常重要的,只有用这种写法才能将计数值最终传递给MainViewModel的构造函数
在“Clear”按钮的点击事件中对计数器进行清零,并且在onPause()方法中对当前的计数进行保存,这样可以保证不管程序是退出还是进入后台,计数都不会丢失
运行程序,点击点击数次“Plus One”按钮,然后退出程序并重新打开,你会发现,计数器的值是不会丢失,如下图:
以上是关于Kotlin基础从入门到进阶系列讲解(进阶篇)Jetpack,(更新中)的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin基础从入门到进阶系列讲解(进阶篇)Android之Activity的生命周期
Kotlin基础从入门到进阶系列讲解(入门篇)Activity的使用
Kotlin基础从入门到进阶系列讲解(基础篇)Fragment的基本使用
Kotlin基础从入门到进阶系列讲解(基础篇)Fragment的基本使用