安卓Jetpack学习 | 青训营笔记

97 阅读5分钟

这是我参与「第四届青训营 」笔记创作活动的第9天

此笔记记录跟随《第一行代码》学习Jetpack过程
1.ViewModel
背景引入: 在安卓配置变更,如改变字体大小。旋转屏幕等情况下,Activity会被重新创建,Activity存储的数据就会被销毁。所以不希望这样的事发生,就把数据存储在ViewModel中,ViewModel在Activity被销毁重建的过程中不会被销毁,自然就能保存数据。
简单来说是这样,实际上不希望Activity干太多的活,又要负责逻辑处理又要负责UI等,所以尽量分离部分工作。
用法:
1.添加依赖 implementation "androidx.lifecycle:lifecycle-extensions:X.X.X”
2.创建类继承ViewModel,声明存储的变量
class MainViewModel : ViewModel() { var counter = 0 }
3.Activity中使用
ViewModelProvider(<你的Activity或Fragment实例>).get(<你的ViewModel>::class.java)
来得到ViewModel实例

之所以要这么写,是因为ViewModel有其独立的生命周期,并且其生命周期要长于Activity。 如果我们在onCreate()方法中创建ViewModel的实例,那么每次onCreate()方法执行的时 候,ViewModel都会创建一个新的实例,这样当手机屏幕发生旋转的时候,就无法保留其中的数据了。

4.Activity类实现对数据操作
书中示例如下:

class MainActivity : AppCompatActivity() {
    lateinit var VM:MainViewModel
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        VM=ViewModelProvider(this).get(mainVM::class.java)
        add.setOnClickListener {            //处理逻辑
            VM.count++
            count.text=VM.count.toString()  //改变TextView中内容
        }
       count.text=VM.count.toString()
    }
}

向ViewModel传递参数

上一小节中创建的MainViewModel的构造函数中没有任何参数,但是思考一下,如果我们确实 需要通过构造函数来传递一些参数,应该怎么办呢?由于所有ViewModel的实例都是通过 ViewModelProvider来获取的,因此我们没有任何地方可以向ViewModel的构造函数中传递参数。

先创建一个工厂类,在该类中构造函数中加上想要的参数,重写create方法,使其返回一个带有参数的ViewModel实例。如下:

class MVMFactory(private val countReserve:Int):ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return MainViewModel(countReserve) as T
    }
}

实现代码(片段):

val countReserved = //参数
//使用ViewModelProvider的另一个构造方法,传入我们自己的工厂类,获得ViewModel
viewModel = ViewModelProvider(this, MainViewModelFactory(countReserved))
.get(MainViewModel::class.java)

2.Lifecycles
背景引入: 同样是为了减少Activity额外负担,我们希望非Activity的类能监听Activity的生命周期,并将其分离出去减少Activity负担。
用法:
1.添加依赖,同ViewModel
2.创建类继承LifecycleObserver,使用注解来实现Activity的生命周期感知,代码如下:

class MyObserver : LifecycleObserver {
    @OnLifecycleEvent(Lifecycle.Event.ON_START)
    fun activityStart() {
        //自定义逻辑
        Log.d("MyObserver", "activityStart")
    }
    @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
    fun activityStop() {
        //自定义逻辑
        Log.d("MyObserver", "activityStop")
    }
}

可以看到,我们在方法上使用了@OnLifecycleEvent注解,并传入了一种生命周期事件。生 命周期事件的类型一共有7种:ON_CREATE、ON_START、ON_RESUME、ON_PAUSE、 ON_STOP和ON_DESTROY分别匹配Activity中相应的生命周期回调;另外还有一种ON_ANY类 型,表示可以匹配Activity的任何生命周期回调。
因此,上述代码中的activityStart()和activityStop()方法就应该分别在Activity的 onStart()和onStop()触发的时候执行。

3.使用LifecycleOwner添加Observer,由于很好的封装,一行代码就可以为Activity添加生命周期的观察者
lifecycle.addObserver(MyObserver())
完成

3.LiveData
背景引入: 虽然未来存在被协程替换的问题,其较低的学习成本等使其依然被广泛使用。最直接明了的使用原因是其可以在后台数据变化的时候主动通知ViewModel,这样便解决了执行操作后UI无法得到及时或正确更新的问题等。
用法:
1.添加依赖,同ViewModel
建议额外添加 implementation "androidx.lifecycle:lifecycle-livedata-ktx:X.X.X"
2.在ViewModel中想存储的变量声明为MutableLiveData对象,泛型类型设为变量类型。如上文例子中改为

    var counter = 0    ->        
    val counter=MutableLiveData<Int>()
    counter.value=0

注意:因为这样外部可以改变LiveData的数据,建议使用如下写法,使外部类只能看不能改:

private val _count= MutableLiveData<Int>()
val counter: LiveData<Int>
     get() = _count

init {
    _count.value=Reservedcount
}
fun plusone(){
     val count=_count.value?:0
     _count.value=count+1
}

也就是说,把MutableLiveData作为private对象,再定义一个LiveData,重写它的get()方法,因为在Observe中观察数据的本质是调用其get方法,所以这里让get方法返回MutableLiveData的值,这样最为规范。
当然这样就不能直接对counter.value进行操作了,所以我们写一个plusone的方法对数据进行改变。

3.在Activity中添加观察者,以观察LiveData变化

VM.counter.observe(this, Observer { counterrr//此处为形参 ->
    countText.text=counterrr.toString()//数据改变时,执行逻辑,形参为LiveData中数据
})

这样Activity在LiveData中数据变化时就会自动执行逻辑了。
不过可以注意到observe方法中没有用到java函数式API的写法,书中解释如下:

这是一种非常特殊的情况,因为observe()方法接收的另一个参数LifecycleOwner也是一个 单抽象方法接口。当一个Java方法同时接收两个单抽象方法接口参数时,要么同时使用函数式 API的写法,要么都不使用函数式API的写法。由于我们第一个参数传的是this,因此第二个参 数就无法使用函数式API的写法了。

所以添加依赖中推荐额外添加一条依赖,这样AS就会自己告诉你这样的写法是多余的,简化后为

VM.counter.observe(this) { counterrr ->
    countText.text = counterrr.toString()
}

4.map和switchMap
背景引入: 基于LiveData,当在复杂的业务环境下,我们可能只关心原始的数据部分数据,或是原始数据的逻辑结果等,需要灵活的转化为另一个新的LiveData,可以使用 map()switchMap() 两个方法。
用法:
考虑到时间成本,就不详细记录这一部分了
值得注意在书中Map部分,改变User的属性,比如firstname等,是无法触发Observe的。因为liveData的对象是User实例,改变其属性是不会改变其应用对象地址的,所以相当于LiveData没更新,自然不会触发Observe。虽然是很简单的坑,我还是掉进去了。