Kotlin Flow常见场景下的使用实例

目录
  • Kotlin Flow在开发中的常用场景使用
  • 一、网络请求搭载Retrofit
    • 1.1 LiveDataCallAdapterFactory
    • 1.2 suspend
  • 二、协程与Flow的选择与差异
  • 三、StateFlow与SharedFlow的选择
  • 总结

Kotlin Flow在开发中的常用场景使用

大家了解了 Flow 的创建与接收流程,了解 SharedFlow 创建的几种方式,各个参数的用途,了解了SharedFlow的 "青春版" StateFlow 的创建与接收,已经他们与 LiveData 的异同。

注:这里青春版打上引号,只是调侃而已,并不是说 StateFlow 比 SharedFlow 更加轻量,而是StateFlow使用更加简单,更加的场景化而已,使用起来感觉比较青春版而已。

那么在真实的开发环境中我们是如何使用Flow的呢?这里从几点举例说明一下。

一、网络请求搭载Retrofit

之前在网上看到有人提问,为什么Retrofit不能返回 Flow 这样的对象。使用一个 FlowCallAdapterFactory 那我就可以直接使用Flow来传递了。

不是不行,有第三方的依赖实现了此功能,为什么官方不出,其实官方已经给出了建议

如果我想使用flow来传递数据,有哪些方式返回Flow的方式呢?这里有几种方案

1.1 LiveDataCallAdapterFactory

Retrofit 增加了 LiveDataCallAdapterFactory,我们可以使用LiveData来包裹对象

interface NewsApi {
    @POST("/wanandroid")
    fun fetchNewsLiveData(
        @FieldMap map:Map<String,String>
    ):LiveData<ApiResponse<NewsBean>>
}

使用的时候

fetchNewsLiveData().asFlow()

这样不就转成了Flow了吗?如果想转为StateFlow 或者SharedFlow,可以继续shareIn stateIn 之类的方法转换为热流。

1.2 suspend

使用挂起函数直接返回对象,然后使用flow函数创建出Flow对象,也是非常的简单,这也是官方推荐的方式。

interface NewsApi {
    @POST("/wanandroid")
   suspend  fun fetchNews(
        @FieldMap map:Map<String,String>
    ):ApiResponse<NewsBean>
}

使用的时候,直接就创建了一个flow对象

flow {
  emit(fetchNews())
}

如果想转为StateFlow 或者SharedFlow,可以继续shareIn stateIn 之类的方法转换为热流。

这种网络数据使用Flow的方式,好处是可以很方便的进行合并,合流,展平等操作,很方便的使用操作符转换成我们想要的数据。

二、协程与Flow的选择与差异

协程与Flow的选择,什么情况下我应该使用协程请求网络,什么情况下我才使用Flow 来操作UI。

其实我们对于实时性不高的数据,我们可以使用 Kotlin 协程处理,而对于实时性较高的数据,我们可以用 Flow 来处理。

例如动态详情顶部是详情数据固定的数据,而底部是列表和点赞评论的数量,这些是动态的数据,那么我们再顶部就可以用协程请求,在底部我们使用Flow处理数据再通知其改变。

    @POST("/wanandroid")
    suspend fun fetchNews(
        @FieldMap map: Map<String, String>
    ): BaseBean<NewsBean>
    suspend fun fetchNewsDetail(): OkResult<NewsBean> {
        return extRequestHttp {
            DemoRetrofit.apiService.fetchNews(
                mapOf("id" to "12232", "key" to "2")
            )
        }
    }
    lifecycleScope.launch {
        val detail = mViewModel.mRepository.fetchNewsDetail()
        detail.checkSuccess {
            updateUI(it)
        }
    }
    private fun updateUI(newsBean: NewsBean?) {
       // XXX
    }

而下面的列表与动态点赞分享数据,我们可以使用 Flow 来操作,当点赞或转发数发生变化时,updateUI() 会被执行,UI根据最新的数据更新。

    private val _stateFlow = MutableStateFlow("")
    val stateFlow: StateFlow<String> = _searchFlow
    fun changeState() {
        viewModelScope.launch {
            val detail = mRepository.changeState()
            detail.checkSuccess {
                //进一系列的数据合流
                //进行一系列的排序、转换之后设置给Flow
                _stateFlow.value = it ?: ""
            }
        }
    }

操作UI的伪代码如下:

    private fun changeData() {
        mViewModel.changeState()
    }
    private fun updateUI() {
       //更新一些UI
    }
    override fun startObserve() {
        lifecycleScope.launchWhenCreated {
            mViewModel.stateFlow.collect {
                updateUI()
            }
        }
    }

是不是静态的页面不能用 Flow ,能不能用 LiveData ? 当然可以用了,上面的只是推荐使用,其他的方式当然都可以例如:

    fun getNewsDetail(): LiveData<NewsBean?> {
        return liveData {
            val detail = mRepository.fetchNewsDetail()
            if (detail is OkResult.Success) {
                emit(detail.data)
            } else {
                emit(null)
            }
        }
    }

使用的时候:

    fun getData(){
        mViewModel.getNewsDetail().observe(this) {
            updateUI()
        }
    }
    private fun updateUI() {
        //更新一些UI
    }

三、StateFlow与SharedFlow的选择

什么时候使用StateFlow ,什么时候使用 SharedFlow ,在之前 SharedFlow 的文章中,我们对比过 StateFlow,SharedFlow,LiveData 的区别。

关于 SharedFlow、StateFlow、LiveData的对比,个人的结论是:根据不同的场景 LiveData StateFlow SharedFlow 都有自己特定的使用场景,谁也无法真的完全平替谁。谁也不是谁的超集,都是各有利弊,按需选择即可。这里不过多赘述。

那其实从另一角度,我们区别不同的场景为状态和事件,看此场景是状态驱动还是事件驱动的。

比如我现在点击了按钮,需要弹窗了,然后使用StateFlow来记录状态,然后收集到这个事件弹出弹框了,然后我们关闭弹窗去浏览此页面的其他信息了了,但是当我们旋转手机屏幕之后,我们会发现弹窗又出来了。这就不合理了。

有同学说,这是StateFlow的问题,此情况我们需要使用LiveData,那LiveData就没有问题了吗?

我们测试一下:

@HiltViewModel
class Demo4ViewModel @Inject constructor(
    val mRepository: Demo5Repository,
    val savedState: SavedStateHandle
) : BaseViewModel() {
    val channel = Channel<String>(Channel.CONFLATED)
    private val _searchLD = MutableLiveData<String>()
    val searchLD: LiveData<String> = _searchLD
    private val _searchFlow = MutableStateFlow("")
    val searchFlow: StateFlow<String> = _searchFlow
    private val _sharedFlow = MutableSharedFlow<String>(replay = 1, onBufferOverflow = BufferOverflow.SUSPEND)
    val sharedFlow: SharedFlow<String> = _sharedFlow
    fun changeSearch(keyword: String) {
        _sharedFlow.tryEmit(keyword)
        _searchFlow.value = keyword
        _searchLD.value = keyword
        channel.trySend(keyword)
    }
}

我们测试 LiveData Channel StateFlow SharedFlow(replay=1)

点击按钮发送事件

旋转屏幕查看Log-3个数据

除了Channel,原来你们都会再次触发,别急我们修改SharedFlow(replay =0)

旋转屏幕查看Log-2个数据

SharedFlow就不会再触发了。

到这里,StateFlow 与 SharedFlow 的使用场景就应该很清晰了,状态(State)用 StateFlow ;事件(Event)用 SharedFlow

关于SateFlow SharedFlow LiveData 的对比可以看这里。

总结

Flow 的使用总的来说还是很广泛,如果你的项目是Kotlin语言开发的,强烈建议使用Flow。

关于LiveData 替换为Flow的问题,这几篇文章也给出了答案,看不同的场景,SateFlow SharedFlow LiveData 各有优缺点,无法真的说谁能真的完全平替谁。

以上就是Kotlin Flow常见场景下的使用实例的详细内容,更多关于Kotlin Flow使用场景的资料请关注我们其它相关文章!

(0)

相关推荐

  • Kotlin协程之Flow基础原理示例解析

    目录 引言 一.Flow的创建 二.Flow的消费 1.SafeFlow类 2.AbstractFlow类 3. SafeCollector类 4.消费过程中的挂起 引言 本文分析示例代码如下: launch(Dispatchers.Main) { flow { emit(1) emit(2) }.collect { delay(1000) withContext(Dispatchers.IO) { Log.d("liduo", "$it") } Log.d(&qu

  • 图解 Kotlin SharedFlow 缓存系统及示例详解

    目录 前言 replay extraBufferCapacity onBufferOverflow SharedFlow Buffer 前言 Kotlin 为我们提供了两种创建“热流”的工具:StateFlow 和 SharedFlow.StateFlow 经常被用来替代 LiveData 充当架构组件使用,所以大家相对熟悉.其实 StateFlow 只是 SharedFlow 的一种特化形式,SharedFlow 的功能更强大.使用场景更多,这得益于其自带的缓存系统,本文用图解的方式,带大家更

  • Kotlin Flow常用封装类StateFlow使用详解

    目录 Kotlin中StateFlow的使用 一.StateFlow的使用 二.替代LiveData 总结 Kotlin中StateFlow的使用 StateFlow 是 Flow 的实现,是一个特殊的流,默认的 Flow 是冷流,而StateFlow 是热流,和 LiveData 比较类似.关于冷热流后面一期 SharedFlow 会详细说明. 使用 StateFlow 替代 LiveData 应该是目前很多开发者的呼吁了,确实 LiveData 的功能 StateFlow 都能实现,可以说是

  • Kotlin Flow封装类SharedFlow StateFlow LiveData使用对比

    目录 Kotlin中SharedFlow的使用 VS StateFlow SharedFlow的特点 一.SharedFlow的使用 二.SharedFlow.StateFlow.LiveData的对比 三.SharedFlow 的粘性设置与事件总线 总结 Kotlin中SharedFlow的使用 VS StateFlow SharedFlow 是继承于 Flow ,同时它是 StateFlow 的父类,它们都是是热流,先说一下冷流与热流的概念. 冷流 :只有订阅者订阅时,才开始执行发射数据流的

  • Kotlin协程Flow生命周期及异常处理浅析

    目录 正文 Flow基本概念 Flow生命周期 处理异常 上游或者中间异常使用catch 下游使用try-catch 切换执行线程 终止操作符 "冷的数据流"从何而来 正文 Kotlin协程中的Flow主要用于处理复杂的异步数据,以一种”流“的方式,从上到下依次处理,和RxJava的处理方式类型,但是比后者更加强大. Flow基本概念 Flow中基本上有三个概念,即 发送方,处理中间层,接收方,可以类比水利发电站中的上游,发电站,下游的概念, 数据从上游开始发送”流淌“至中间站被”处理

  • Kotlin Flow操作符及基本使用详解

    目录 一.Flow的基本概念 二.Flow的生命周期与异常处理 2.1 开始与结束 2.2 异常的处理 2.3 retry的处理 2.4 超时的处理 2.5 Flow的取消 三.Flow的创建方式 四.Flow的接收方式 五.Flow的转换操作符 5.1 基本操作符 5.2 特殊操作符 5.3 组合与展平操作符 5.4 切换线程 总结 一.Flow的基本概念 Kotlin 的 Flow 相信大家都或多或少使用过,毕竟目前比较火,目前我把Flow的使用整理了一下.希望和大家所学对照一下,能有所启发

  • Kotlin Flow常见场景下的使用实例

    目录 Kotlin Flow在开发中的常用场景使用 一.网络请求搭载Retrofit 1.1 LiveDataCallAdapterFactory 1.2 suspend 二.协程与Flow的选择与差异 三.StateFlow与SharedFlow的选择 总结 Kotlin Flow在开发中的常用场景使用 大家了解了 Flow 的创建与接收流程,了解 SharedFlow 创建的几种方式,各个参数的用途,了解了SharedFlow的 "青春版" StateFlow 的创建与接收,已经他

  • JavaScript解构赋值的5个常见场景与实例教程

    目录 前言 1. 提取数据 2. 别名取值 3. 动态属性 4. 对象解构中的 Rest 5. 默认值 总结 前言 解构赋值语法是一种 JavaScript 表达式,通过解构赋值, 可以将属性/值从对象/数组中取出,赋值给其他变量.这种语法是 ECMAscript 6 规范引入了一种新语法,可以更轻松地从数组和对象中获取值. 1. 提取数据 先来看看如何在 JavaScript 中解构对象,可以从这个商品对象的简单示例开始. const product = { id: 1, title: "Ni

  • 深入浅析Vue不同场景下组件间的数据交流

    正文 浅谈Vue不同场景下组件间的数据"交流" Vue的官方文档可以说是很详细了.在我看来,它和react等其他框架文档一样,讲述的方式的更多的是"方法论",而不是"场景论",这也就导致了:我们在阅读完文档许多遍后,写起代码还是不免感到有许多困惑,因为我们不知道其中一些知识点的运用场景.这就是我写这篇文章的目的,探讨不同场景下组件间的数据"交流"的Vue实现 父子组件间的数据交流 父子组件间的数据交流可分为两种: 1.父组件传

  • JavaScript简单下拉菜单实例代码

    本文实例讲述了JavaScript简单下拉菜单实例代码.分享给大家供大家参考.具体如下: 这是一款JavaScript实现的下拉菜单演示代码,带渐变效果的CSS+jQuery菜单,向下滑出型的菜单,最高支持两级,网上常见到的一种菜单风格,希望大家喜欢哦. 运行效果截图如下: 在线演示地址如下: http://demo.jb51.net/js/2015/js-simple-xlcd-down-menu-codes/ 具体代码如下: <!DOCTYPE html PUBLIC "-//W3C/

  • Kotlin 内联函数详解及实例

    Kotlin 内联函数详解及实例 概述 在说内联函数之前,先说说函数的调用过程. 调用某个函数实际上将程序执行顺序转移到该函数所存放在内存中某个地址,将函数的程序内容执行完后,再返回到转去执行该函数前的地方.这种转移操作要求在转去前要保护现场并记忆执行的地址,转回后先要恢复现场,并按原来保存地址继续执行.也就是通常说的压栈和出栈.因此,函数调用要有一定的时间和空间方面的开销.那么对于那些函数体代码不是很大,又频繁调用的函数来说,这个时间和空间的消耗会很大. 那怎么解决这个性能消耗问题呢,这个时候

  • Vue实现购物车场景下的应用

    本文实例为大家分享了Vue在购物车场景下的应用,供大家参考,具体内容如下 购物车场景需求: 1. 商品.店铺.购物车的选择 2. 商品删除 关键代码 测试数据 var _list = [{ checked: false, goods: [{ name: "商品1", price: 23, checked: false }] }, { checked: false, goods: [{ name: "商品2", price: 20, checked: false },

  • MySQL中索引失效的常见场景与规避方法

    前言 之前有看过许多类似的文章内容,提到过一些sql语句的使用不当会导致MySQL的索引失效.还有一些MySQL"军规"或者规范写明了某些sql不能这么写,否则索引失效. 绝大部分的内容笔者是认可的,不过部分举例中笔者认为用词太绝对了,并没有说明其中的原由,很多人不知道为什么.所以笔者绝对再整理一遍MySQL中索引失效的常见场景,并分析其中的原由供大家参考. 当然请记住,explain是一个好习惯! MySQL索引失效的常见场景 在验证下面的场景时,请准备足够多的数据量,因为数据量少时

  • Kotlin中常见的符号详解

    前几年的Google I/O大会上,Google正式宣布,Kotlin将会成为Android开发的官方支持语言.除了Android外,Kotlin还可以完全作为服务端开发的语言,比如在未来的Spring 5就将对Kotlin提供强大的支持.以及浏览器编程语言,与JS进行交互. Kotlin是一门静态语言,支持多种平台,包括移动端.服务端以及浏览器端,此外,Kotlin还是一门融合了面向对象与函数式编程的语言,支持泛型.安全的空判断,并且Kotlin与Java可以做到完全的交互. 现在介绍Kotl

随机推荐