Jetpack的学习

一、ViewModel

ViewModel的一个重要作用就是帮助Activity分担一部分的工作,专门存放关于界面相关的数据。只要界面上能看到的数据,都应该存放到ViewModel中。另外一个很重要的特性就是当手机屏幕发生旋转时不会被重新创建,只有当acitivity被退出是跟着一起销毁。

1.基本用法

一般每一个Activity和Fragment都会创建一个对应的ViewModel。

下面是一个简单的计数器的例子:

1.导包:
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'

2.xml界面 简单的按钮和textView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/info_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        android:textSize="30sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.519"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.123" />

    <Button
        android:id="@+id/button"
        android:layout_width="403dp"
        android:layout_height="52dp"
        android:text="Button"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.52"
        app:layout_constraintStart_toStartOf="parent"
        tools:layout_editor_absoluteY="187dp"
        tools:ignore="MissingConstraints" />

</androidx.constraintlayout.widget.ConstraintLayout>
3.创建MianViewModel
import androidx.lifecycle.ViewModel

class MainViewModel : ViewModel() {
    var counter = 0
}
4.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)
        button.setOnClickListener {
            viewModel.counter++
            refreshCounter()
        }
        refreshCounter()
    }

    private fun refreshCounter() {
        info_text.text = viewModel.counter.toString()
    }
}

需要注意的点:

不可以在onCreate()中创建viewModel的实例,一定要使用ViewModelProvider来获取。因为ViewModel有其独立的生命周期,在onCreate中直接创建,会每次都创建一个实例,如果屏幕发生旋转就无法保留其自己的数据。

2.向ViewModel中传递参数

向ViewModel中传递参数,我们需要借助ViewModelProvider.Factory。

1.首先修改ViewModel,构造函数中添加一个参数
class MainViewModel(countReserved: Int) : ViewModel() {
    var counter = countReserved
}
2.创建一个MainViewModelFactory
class MainViewModelFactory(private val countReserved: Int) : ViewModelProvider.Factory {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        return MainViewModel(countReserved) as T
    }
}
3.修改MianActivity中的代码
class MainActivity : AppCompatActivity() {
    lateinit var viewModel: MainViewModel
    lateinit var sp: SharedPreferences//使用SharedPreference用于恢复数据
    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)

        btn_add_count.setOnClickListener {
            viewModel.counter++
            refreshCounter()
        }
        btn_clear.setOnClickListener {
            viewModel.counter = 0
            refreshCounter()
        }
        refreshCounter()
    }

    private fun refreshCounter() {
        tv_info.text = viewModel.counter.toString()
    }

    override fun onPause() {
        super.onPause()
        sp.edit {//将数据存储到SharedPreference
            putInt("count_reserved", viewModel.counter)
        }
    }
}

 

二、Lifecycles

Lifecycles是为了让任何一个类轻松感知Activity的生命周期。使用方法也是非常简单。

1.首先实现LifecycleObserver的接口
class MyObserver : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun onActivityStart(){
        Log.d("MyObserver","activity start")
    }
}
2.如果Activity继承自AppCompatActivity
添加:lifecycle.addObserver(MyObserver())

如果想要获取当前Activity的生命周期状态,则在MyObserver的构造函数中将Lifecycle对象传进来即可

class MyObserver(val lifecycle: Lifecycle) : LifecycleObserver {
        ...
        lifecycle.currentState//即可获取当前生命周期状态
    
}

三、LiveData

1.LiveData通常是和ViewModel一起使用。LiveData可以包含任意类型的数据,并在数据发生变化时通知给观察者。下面我们将上面的计数器使用LiveData对数据进行包装,然后在Activity中观察。

1.修改MainViewModel
class MainViewModel(countReserved: Int) : ViewModel() {
    private val _counter = MutableLiveData<Int>()
    val counter: LiveData<Int>
        get() = _counter

    init {
        _counter.value = countReserved
    }

    fun plusOne() {
        val count = _counter.value ?: 0
        _counter.value = count + 1
    }

    fun clear() {
        _counter.value = 0
    }
}
2.修改MainActivity中的代码
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycle.addObserver(MyObserver(lifecycle))
        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)
        btn_add_count.setOnClickListener {
            viewModel.plusOne()
        }
        btn_clear.setOnClickListener {
            viewModel.clear()
        }
        viewModel.counter.observe(this, Observer { count ->
            tv_info.text = count.toString()
        })
    }

需要注意的是:

使用LiveData设置数据时有setValue和postValue两种方法,如果是在子线程中设置数据则需要调用postValue,否则将报错。

上面的例子,将_counter设置为私有类型是为了不将LiveData中的数据暴露给外部,防止外部对数据进行修改。推荐的做法是,永远暴漏不可变的部分给ViewModel外部,只能观察数据的变化,设置LiveData的操作只能在ViewModel内部完成。

2.map和switchmap

(1)map

map()方法是将实际包含数据的LiveData和仅用于观察的LiveData进行转换。

例如:我们有一个User的类:

data class User(var firstName: String, var lastName: String, var age: Int)

但是在使用时我们仅需要获取名字,完全不关心用户的年龄,这是我们就不需要将整个User类全部暴漏到外部,下面我们修改一下ViewModel中的代码:

class MainViewModel(countReserved: Int) : ViewModel() {

    private val userLiveData = MutableLiveData<User>()
    val userName: LiveData<String> = Transformations.map(userLiveData) { user ->
        "${user.firstName} ${user.lastName}"
    }
    ...
}

(2)swithmap

switchmap的应用场景比较单一,如果ViewModel中某个LiveData对象是调用另外的方法获取的,那么我们需要借助switchmap方法,将这个LiveData的对象转换为可观察的LiveData对象。

1.创建一个单例类
object Repository {
    fun getUser(userId: String): LiveData<User> {
        var liveData = MutableLiveData<User>()
        liveData.value = User(userId, userId, 0)
        return liveData
    }
}
2.在ViewModel中添加代码
    fun getUser(userId: String): LiveData<User> {
        return Repository.getUser(userId)
    }

但是此时需要注意的是,我们每次调用getUser()时都会获取一个新的实例,根本无法观察的数据的变化,此时我们需要使用switchmap将这个LiveData的对象转换成为一个可观察的LiveData对象:

    private val userIdLiveData = MutableLiveData<String>()
    val user: LiveData<User> = Transformations.switchMap(userIdLiveData) { userId ->
        Repository.getUser(userId)
    }

    fun getUser(userId: String) {
        userIdLiveData.value = userId
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值