Android进阶之路Android开发经验谈Android技术知识

Jetpack架构组件 — LiveData与ViewModel

2020-08-22  本文已影响0人  木木玩Android

为什么要使用ViewModelLiveData,它有哪些优势?

ViewModel将视图和逻辑进行了分离。Activity或者Fragment只负责UI显示部分。具体的网络请求或者数据库操作则有ViewModel负责。类似于MVP模式中的Presenter层。ViewModel类旨在以注重生命周期的方式存储和管理界面相关的数据。让数据可在发生屏幕旋转等配置更改后继续留存。我们知道类似旋转屏幕等配置项改变会导致我们的 Activity 被销毁并重建,此时 Activity 持有的数据就会跟随着丢失,而ViewModel 则并不会被销毁,从而能够帮助我们在这个过程中保存数据。并且ViewModel不持有View层的实例,通过LiveDataActivity或者Fragment通讯,不用担心潜在的内存泄漏问题

LiveData 是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 ActivityFragmentService)的生命周期。这种感知能力可确保LiveData当数据源发生变化的时候,通知它的观察者更新UI界面。同时它只会通知处于Active状态的观察者更新界面,如果某个观察者的状态处于PausedDestroyed时那么它将不会收到通知。所以不用担心内存泄漏问题。

说明 ViewModel 随着 Activity 状态的改变而经历的生命周期。

简单使用

def lifecycle_version = "2.2.0"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"

复制代码
class MyViewModel : ViewModel() {
    private val currentName by lazy { MutableLiveData<String>().also { loadData() } }

    fun getCurrentName(): LiveData<String> = currentName

    fun loadData() {
        viewModelScope.launch {
            try {
                var i = 0
                while (isActive) {
                    delay(2000L)
                    currentName.value = "AAPL$i"
                    i++
                }
            } catch (e: Throwable) {
                e.printStackTrace()
            }
        }
    }
} 
复制代码
private val myViewModel by lazy { ViewModelProvider(this, ViewModelProvider.AndroidViewModelFactory.getInstance(application)).get(MyViewModel::class.java) }

private fun initViewModel() {
    myViewModel.getCurrentName().observe(this, Observer {
        Log.i(TAG, it)
    })
}
复制代码

LiveData的转换

LiveDatamapswitchMap方法来完成LiveData的转换

map只是对LiveData里面的值进行转换,switchMap是直接对LiveData进行转化。类似于RxJavamapflatMap的区别

class MyViewModel : ViewModel() {

    val currentName by lazy { MutableLiveData<String>() }

    fun getCurrentName(): LiveData<String> = currentName.map {
        it+ " adddd"
    }

    fun loadData() {
        currentName.value = "AAPL  DDD"
    }
}
复制代码

switchMap的使用

class MyViewModel : ViewModel() {
    val itemId by lazy { MutableLiveData<String>() }

    fun getCurrentName(): LiveData<String> = itemId.switchMap {
        liveData { emit(getSymbol(it)) }
    }

    private suspend fun getSymbol(id: String): String {
        delay(1000L)
        if (id == "9131313131"){
          return "AAPL "
        } 
        return "Google"
    }
}
复制代码

ViewModel中使用协程

引入依赖

// ViewModel中内置协程
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
复制代码

扩展了viewModelScope属性,上下文同MainScope()

CloseableCoroutineScope

val ViewModel.viewModelScope: CoroutineScope
        get() {
            val scope: CoroutineScope? = this.getTag(JOB_KEY)
            if (scope != null) {
                return scope
            }
            return setTagIfAbsent(JOB_KEY,
                CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate))
        }

internal class CloseableCoroutineScope(context: CoroutineContext) : Closeable, CoroutineScope {
    override val coroutineContext: CoroutineContext = context
    // 会在ViewModel的onCleard()方法中调用协程的cancel方法。也就是在Activity或Fragment的onDestory中调用
    override fun close() {
        coroutineContext.cancel()
    }
}
复制代码

viewModelScope会在ViewModelonCleard()方法中调用协程的cancel方法。也就是在ActivityFragmentonDestory中调用,不需要我们手动去cancel协程

LiveData配合协程的使用

引入依赖

// LiveData中内置协程
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
复制代码
internal class LiveDataScopeImpl<T>(
    internal var target: CoroutineLiveData<T>,
    context: CoroutineContext
) : LiveDataScope<T> {

    override val latestValue: T?
        get() = target.value

  // use `liveData` provided context + main dispatcher to communicate with the target
  // LiveData. This gives us main thread safety as well as cancellation cooperation
  // liveData构造的LiveData 观察者线程为主线程
  private val coroutineContext = context + Dispatchers.Main.immediate

    override suspend fun emitSource(source: LiveData<T>): DisposableHandle =
        withContext(coroutineContext) {
            return@withContext target.emitSource(source)
        }

    override suspend fun emit(value: T) = withContext(coroutineContext) {
        target.clearSource()
        target.value = value
    }
}
复制代码

同样的操作可以用更精简的方法来完成。也就是使用liveData协程构造方法。liveData协程构造方法构造出来的LiveData观察者直接是主线程

class MyViewModel : ViewModel() {
    private val mutableCurrentName = liveData(Dispatchers.IO) {
        emit(getSymbol())
    }

    val currentName: LiveData<String> = mutableCurrentName

    private suspend fun getSymbol(): String {
        delay(1000L)
        return "AAPL"
    }
}
复制代码

liveData协程构造方法提供了一个协程代码块参数,当LiveData被观察时,里面的操作就会执行。LiveData 协程构造方法还可以接收一个 Dispatcher 作为参数,这样就可以将这个协程移至另一个线程。

另外,还可以使用 emitSource() 方法从另一个 LiveData 获取更新的结果

liveData(Dispatchers.IO) {
    emit(LOADING_STRING)
    emitSource(dataSource.fetchWeather())
}
复制代码

一个正常的网络请求场景

class MyViewModel : ViewModel() {
    private val repository by lazy { DataRepository() }

    val currentName = liveData {
        try {
            emit(repository.sendNetworkRequestSuspend())
        } catch (e: Throwable) {
            e.printStackTrace()
        }
    }
}

interface SplashInterface {
    // 协程的suspend
    @GET("/repos/{owner}/{repo}")
    suspend fun contributors(@Path("owner") owner: String,
                             @Path("repo") repo: String): Repository
}

class DataRepository {
    private val apiService by lazy {
        RetrofitHelper.getInstance().createService(SplashInterface::class.java)
    }

    suspend fun sendNetworkRequestSuspend(): Repository {
        return apiService.contributors("square", "retrofit")
    }
}

写在最后


我意识到有很多经验和知识值得分享给大家,也可以通过我们的能力和经验解答大家在IT学习中的很多困惑,所以在工作繁忙的情况下还是坚持各种整理和分享。但苦于知识传播途径有限,很多程序员朋友无法获得正确的资料得到学习提升,故此将并将重要的Android进阶资料包括自定义view、性能优化、MVC与MVP与MVVM三大框架的区别、NDK技术、阿里面试题精编汇总、常见源码分析等学习资料免费分享出来。

知识不体系?这里还有整理出来的Android进阶学习的思维脑图,给大家参考一个方向。包含知识脉络 + 诸多细节,由于篇幅有限,下面只是以图片的形式给大家展示一部分。

Android学习PDF+学习视频+面试文档+知识点笔记

【Android高级架构视频学习资源】

Android部分精讲视频领取学习后更加是如虎添翼!进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

【Android进阶学习视频】、【全套Android面试秘籍】可以简信我【学习】查看免费领取方式!
原文链接:https://juejin.im/post/6863031989155889166

上一篇下一篇

猜你喜欢

热点阅读