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的基本使用

Kotlin基础从入门到进阶系列讲解(入门篇)Activity的简介和使用

Kotlin基础从入门到进阶系列讲解(入门篇) 文件存储的基本使用