HarmonyOS鸿蒙最全Kotlin flow实践总结_flow combine(2),2024年最新面试经历的文章

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

            fetchDataFromLocal().also {
                Log.d(TAG,"fetchDataFromLocal result:$it , thread:${Thread.currentThread().name}")
                //本地数据加载成功  
                if (it is CResult.Success) {
                    send(it)
                }
            }
        }

        val dataFromNetDeffer = async {
            fetchDataFromNetWork().also {
                Log.d(TAG,"fetchDataFromNetWork result:$it , thread:${Thread.currentThread().name}")
                //网络数据加载成功  
                if (it is CResult.Success) {
                    send(it)
                    //如果网络数据已加载,可以直接取消任务,就不需要处理本地数据了
                    dataFromLocalDeffer.cancel()
                }
            }
        }

        //本地数据和网络数据,都加载失败的情况
        val localData = dataFromLocalDeffer.await()
        val networkData = dataFromNetDeffer.await()
        if (localData is CResult.Error && networkData is CResult.Error) {
            send(CResult.Error(Throwable("load data error")))
        }
    }
}

protected abstract suspend fun fetchDataFromLocal(): CResult<T>

protected abstract suspend fun fetchDataFromNetWork(): CResult<T>

}

sealed class CResult {
data class Success(val data: T) : CResult()
data class Error(val throwable: Throwable) : CResult()
}


#### 测试验证


写个TestRepositity,实现CacheRepositity的抽象方法。  
 通过delay延迟耗时来模拟各种场景,观察日志的输出顺序。



private fun cacheRepositityDemo(){
val repositity=TestRepositity()
lifecycleScope.launch {
repositity.getData().onStart {
Log.d(TAG, “TestRepositity: onStart”)
}.onCompletion {
Log.d(TAG, “TestRepositity: onCompletion”)
}.collect {
Log.d(TAG, “collect: $it”)
}
}
}


##### 本地数据比网络数据加载快



class TestRepositity : CacheRepositity() {
override suspend fun fetchDataFromLocal(): CResult {
delay(1000)
return CResult.Success(“data from fetchDataFromLocal”)
}

override suspend fun fetchDataFromNetWork(): CResult<String> {
    delay(2000)
    return CResult.Success("data from fetchDataFromNetWork")
}

}


模拟数据:本地加载delay1秒,网络加载delay2秒  
 日志输出:collect 执行两次,先收到本地数据,再收到网络数据。



onStart
fetchDataFromLocal result:Success(data=data from fetchDataFromLocal) , thread:main
collect: Success(data=data from fetchDataFromLocal)
fetchDataFromNetWork result:Success(data=data from fetchDataFromNetWork) , thread:main
collect: Success(data=data from fetchDataFromNetWork)
onCompletion


##### 网络数据比本地数据加载快



class TestRepositity : CacheRepositity() {
override suspend fun fetchDataFromLocal(): CResult {
delay(2000)
return CResult.Success(“data from fetchDataFromLocal”)
}

override suspend fun fetchDataFromNetWork(): CResult<String> {
    delay(1000)
    return CResult.Success("data from fetchDataFromNetWork")
}

}


模拟数据:本地加载delay 2秒,网络加载delay 1秒  
 日志输出:collect 只执行1次,只收到网络数据。



onStart
fetchDataFromNetWork result:Success(data=data from fetchDataFromNetWork) , thread:main
collect: Success(data=data from fetchDataFromNetWork)
onCompletion


##### 网络数据加载失败,使用本地数据



class TestRepositity : CacheRepositity() {
override suspend fun fetchDataFromLocal(): CResult {
delay(2000)
return CResult.Success(“data from fetchDataFromLocal”)
}

override suspend fun fetchDataFromNetWork(): CResult<String> {
    delay(1000)
    return CResult.Error(Throwable("fetchDataFromNetWork Error"))
}

}


模拟数据:本地加载delay 2秒,网络数据加载失败  
 日志输出:collect 只执行1次,只收到本地数据。



onStart
fetchDataFromNetWork result:Error(throwable=java.lang.Throwable: fetchDataFromNetWork Error) , thread:main
fetchDataFromLocal result:Success(data=data from fetchDataFromLocal) , thread:main
collect: Success(data=data from fetchDataFromLocal)
onCompletion


##### 网络数据和本地数据都加载失败



class TestRepositity : CacheRepositity() {
override suspend fun fetchDataFromLocal(): CResult {
delay(2000)
return CResult.Error(Throwable(“fetchDataFromLocal Error”))
}

override suspend fun fetchDataFromNetWork(): CResult<String> {
    delay(1000)
    return CResult.Error(Throwable("fetchDataFromNetWork Error"))
}

}


模拟数据:本地数据加载失败,网络数据加载失败  
 日志输出: collect 只执行1次,结果是CResult.Error,代表加载数据失败。



onStart
fetchDataFromNetWork result:Error(throwable=java.lang.Throwable: fetchDataFromNetWork Error) , thread:main
fetchDataFromLocal result:Error(throwable=java.lang.Throwable: fetchDataFromLocal Error) , thread:main
collect: Error(throwable=java.lang.Throwable: load data error)
onCompletion


### 场景三:多种数据源,按照顺序合并进行展示


![Image.png](https://img-blog.csdnimg.cn/img_convert/9abbe46082c8bf48d57b81cb8bf41c1f.png#pic_center)


在实际的开发场景中,经常一个页面的数据,是需要发起多个网络请求之后,组合数据之后再进行显示。 比如类似这种页面,3种数据,需要由3个网络请求获取得到,然后再进行相应的显示。


实现目标:


1. 接口间不需要互相等待,哪些数据先回来,就先展示哪部分
2. 控制数据的显示顺序


##### flow combine操作符


可以合并多个不同的 Flow 数据流,生成一个新的流。 只要其中某个子 Flow 数据流有产生新数据的时候,就会触发 combine 操作,进行重新计算,生成一个新的数据。


##### 例子



class HomeViewModel : ViewModel() {

//暴露给View层的列表数据
val list = MutableLiveData<List<String?>>()

//多个子Flow,这里简单都返回String,实际场景根据需要,返回相应的数据类型即可
private val bannerFlow = MutableStateFlow<String?>(null)
private val channelFlow = MutableStateFlow<String?>(null)
private val listFlow = MutableStateFlow<String?>(null)


init {
    //使用combine操作符
    viewModelScope.launch {
        combine(bannerFlow, channelFlow, listFlow) { bannerData, channelData, listData ->
            Log.d("HomeViewModel", "combine  bannerData:$bannerData,channelData:$channelData,listData:$listData")
            //只要子flow里面的数据不为空,就放到resultList里面
            val resultList = mutableListOf<String?>()
            if (bannerData != null) {
                resultList.add(bannerData)
            }
            if (channelData != null) {
                resultList.add(channelData)
            }
            if (listData != null) {
                resultList.add(listData)
            }
            resultList
        }.collect {
            //收集combine之后的数据,修改liveData的值,通知UI层刷新列表
            Log.d("HomeViewModel", "collect: ${it.size}")
            list.postValue(it)
        }
    }
}

fun loadData() {
    viewModelScope.launch(Dispatchers.IO) {
        //模拟耗时操作
        async {
            delay(1000)
            Log.d("HomeViewModel", "getBannerData success")
            bannerFlow.emit("Banner")
        }
        async {
            delay(2000)
            Log.d("HomeViewModel", "getChannelData success")
            channelFlow.emit("Channel")
        }
        async {
            delay(3000)
            Log.d("HomeViewModel", "getListData success")
            listFlow.emit("List")
        }
    }
}

}


HomeViewModel


1. 提供一个 LiveData 的列表数据给View层使用
2. 内部有3个子 flow ,分别负责相应数据的生产。(这里简单都返回String,实际场景根据需要,返回相应的数据类型即可)。
3. 通过 combine 操作符,组合这3个子flow的数据。
4. collect 接收生成的新数据,并修改liveData的数据,通知刷新UI


View层使用



private fun flowCombineDemo() {
val homeViewModel by viewModels()
homeViewModel.list.observe(this) {
Log.d(“HomeViewModel”, “observe size:${it.size}”)
}

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!


img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

8910224b259a5ffe804fa6d0db.png)
[外链图片转存中…(img-YdJ71Gxf-1715821503832)]
[外链图片转存中…(img-UpVWLsI3-1715821503833)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上鸿蒙开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化的资料的朋友,可以戳这里获取

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值