美文网首页
协程之Flow

协程之Flow

作者: lllllittlep | 来源:发表于2021-12-17 19:10 被阅读0次

    Flow是什么

    官方文档给予了一句话简单的介绍:

    Flow — cold asynchronous stream with flow builder and comprehensive operator set (filter, map, etc);

    翻译一下:

    具有流构建器和综合运算符集(过滤器、映射等)的冷异步流;

    为了便于理解我们先简单看一下它的结构:

    他的Rxjava类似都是上游产生数据,操作符,下游接受数据这种基于数据流驱动的结构。为了便于理解和上手,我做了一些flow和rxjava的使用类比。但需要注意flow是协程中的库,只能用于协程环境。

    再熟悉一下Rxjava的定义:

    RxJava:a library for composing asynchronous and event-based programs using observable sequences for the Java VM

    翻译:RxJava是一个在 Java VM 上使用可观测的序列来组成异步的、基于事件的程序的库

    总结:RxJava是一个 基于事件流、实现异步等操作的库

    [if !supportLists]二.[endif]初识flow

    2.1简单创建

    第一步:创建一个flow

    val flow1 = flow {emit(1)}

    第二步:收集数据流

    flow1.collect {print(it)}

    类比Rxjava

    第一步:创建一个被观察者

    val observable = Observable.create<Int> { emitter ->

                emitter.onNext(1)

            }

    第二步:观察者订阅被观察者

    observable.subscribe(Consumer {

                print(it)

            });

    2.2其他创建方式

    第一种

    [if !supportLists]1. [endif] flowOf(1,2,3,4,5)

    [if !supportLists]2. [endif]        .onEach {

    [if !supportLists]3. [endif]            delay(100)

    [if !supportLists]4. [endif]        }

    [if !supportLists]5. [endif]        .collect{

    [if !supportLists]6. [endif]            println(it)

    [if !supportLists]7. [endif]        }

    类比rxjava

    val observable: Observable<*> = Observable.just("A", "B", "C")

    第二种

    listOf(1, 2, 3, 4, 5).asFlow()

    类比rxjava

    val observable1: Observable<String> = Observable.fromIterable(words)

    2.3线程切换

    [if !supportLists]1. [endif] flow {

    [if !supportLists]2. [endif]        for (i in 1..5) {

    [if !supportLists]3. [endif]            delay(100)

    [if !supportLists]4. [endif]            emit(i)

    [if !supportLists]5. [endif]        }

    [if !supportLists]6. [endif]    }.map {

    [if !supportLists]7. [endif]        it * it

    [if !supportLists]8. [endif]    }.flowOn(Dispatchers.IO)

    [if !supportLists]9. [endif]   .collect {

    [if !supportLists]10. [endif]            println(it)

    [if !supportLists]11. [endif]    }

    类比rxjava

    [if !supportLists]1. [endif]Observable.just("A", "B", "C")

    [if !supportLists]2. [endif]            .subscribeOn(Schedulers.io())

    [if !supportLists]3. [endif]            .observeOn(AndroidSchedulers.mainThread())

    [if !supportLists]4. [endif]            .subscribe(object : Observer<Any?> {

    [if !supportLists]5. [endif]            override fun onSubscribe(d: Disposable) {

    [if !supportLists]6. [endif]            }

    [if !supportLists]7. [endif]            override fun onNext(t: Any) {

    [if !supportLists]8. [endif]                print(t)

    [if !supportLists]9. [endif]            }

    [if !supportLists]10. [endif]            override fun onError(e: Throwable) {

    [if !supportLists]11. [endif]            }

    [if !supportLists]12. [endif]            override fun onComplete() {

    [if !supportLists]13. [endif]            }

    [if !supportLists]14. [endif]        });

    这里面值得注意的一个点是

    flow builder 和 map 操作符都会受到 flowOn 的影响。

    而collect() 指定哪个线程,则需要看整个 flow 处于哪个 CoroutineScope 下。

    2.4取消

    这里和rxjava有些不同

    如果 flow 是在一个挂起函数内被挂起了,那么 flow 是可以被取消的,否则不能取消。也就是说flow的取消,要搭配协程的取消

    [if !supportLists]1. [endif]lifecycleScope.launch {

    [if !supportLists]2. [endif]            withTimeoutOrNull(2500) {

    [if !supportLists]3. [endif]                flow {

    [if !supportLists]4. [endif]                    for (i in 1..5) {

    [if !supportLists]5. [endif]                        delay(1000)

    [if !supportLists]6. [endif]                        emit(i)

    [if !supportLists]7. [endif]                    }

    [if !supportLists]8. [endif]                }.collect {

    [if !supportLists]9. [endif]                    println(it)

    [if !supportLists]10. [endif]                }

    [if !supportLists]11. [endif]            }

    [if !supportLists]12. [endif]            println("Done")

    [if !supportLists]13. [endif]        }

    rxjava的取消

    [if !supportLists]1. [endif]Observable.interval(0,1000, TimeUnit.MILLISECONDS)

    [if !supportLists]2. [endif]            .observeOn(AndroidSchedulers.mainThread())

    [if !supportLists]3. [endif]            .subscribe(object : Observer<Long?> {

    [if !supportLists]4. [endif]                var d : Disposable? = null

    [if !supportLists]5. [endif]                override fun onSubscribe(d: Disposable) {

    [if !supportLists]6. [endif]                    this.d = d

    [if !supportLists]7. [endif]                }

    [if !supportLists]8. [endif]                override fun onNext(t: Long) {

    [if !supportLists]9. [endif]                    println(t)

    [if !supportLists]10. [endif]                    if(t>10){

    [if !supportLists]11. [endif]                        d?.dispose()

    [if !supportLists]12. [endif]                    }

    [if !supportLists]13. [endif]                }

    [if !supportLists]14. [endif]                override fun onError(e: Throwable) {

    [if !supportLists]15. [endif]                }

    [if !supportLists]16. [endif]                override fun onComplete() {

    [if !supportLists]17. [endif]                }

    [if !supportLists]18. [endif]            })

    2.5生命周期状态回调

    [if !supportLists]1. [endif]lifecycleScope.launch {

    [if !supportLists]2. [endif]            val time = measureTimeMillis {

    [if !supportLists]3. [endif]                flow {

    [if !supportLists]4. [endif]                    for (i in 0..3) {

    [if !supportLists]5. [endif]                        emit(i.toString())

    [if !supportLists]6. [endif]                    }

    [if !supportLists]7. [endif]                    throw Exception("Test")

    [if !supportLists]8. [endif]                }.onStart {

    [if !supportLists]9. [endif]                    Log.d("xys", "Start Flow in ${Thread.currentThread().name}")

    [if !supportLists]10. [endif]                }.onEach {

    [if !supportLists]11. [endif]                    Log.d("xys", "emit value---$it")

    [if !supportLists]12. [endif]                }.onCompletion {

    [if !supportLists]13. [endif]                    Log.d("xys", "Flow Complete")

    [if !supportLists]14. [endif]                }.collect {

    [if !supportLists]15. [endif]                    Log.d("xys", "Result---$it")

    [if !supportLists]16. [endif]                }

    [if !supportLists]17. [endif]            }

    [if !supportLists]18. [endif]            Log.d("xys", "Time---$time")

    [if !supportLists]19. [endif]        }

    [if !supportLists]五.[endif]重试

    [if !supportLists]1. [endif]lifecycleScope.launch {

    [if !supportLists]2. [endif]            flow {

    [if !supportLists]3. [endif]                for (i in 0..3) {

    [if !supportLists]4. [endif]                    emit(i.toString())

    [if !supportLists]5. [endif]                }

    [if !supportLists]6. [endif]            }.retryWhen { _, retryCount ->

    [if !supportLists]7. [endif]                retryCount <= 3

    [if !supportLists]8. [endif]            }.onStart {

    [if !supportLists]9. [endif]                Log.d("xys", "Start Flow in ${Thread.currentThread().name}")

    [if !supportLists]10. [endif]            }.onEach {

    [if !supportLists]11. [endif]                Log.d("xys", "emit value---$it")

    [if !supportLists]12. [endif]            }.onCompletion {

    [if !supportLists]13. [endif]                Log.d("xys", "Flow Complete")

    [if !supportLists]14. [endif]            }.collect {

    [if !supportLists]15. [endif]                Log.d("xys", "Result---$it")

    [if !supportLists]16. [endif]            }

    [if !supportLists]17. [endif]        }

    [if !supportLists]六.[endif]异常捕获

    [if !supportLists]1. [endif]lifecycleScope.launch {

    [if !supportLists]2. [endif]            flow {

    [if !supportLists]3. [endif]                for (i in 0..3) {

    [if !supportLists]4. [endif]                    emit(i.toString())

    [if !supportLists]5. [endif]                }

    [if !supportLists]6. [endif]                throw Exception("Test")

    [if !supportLists]7. [endif]            }.retryWhen { _, retryCount ->

    [if !supportLists]8. [endif]                retryCount <= 3

    [if !supportLists]9. [endif]            }.onStart {

    [if !supportLists]10. [endif]                Log.d("xys", "Start Flow in ${Thread.currentThread().name}")

    [if !supportLists]11. [endif]            }.onEach {

    [if !supportLists]12. [endif]                Log.d("xys", "emit value---$it")

    [if !supportLists]13. [endif]            }.onCompletion {

    [if !supportLists]14. [endif]                Log.d("xys", "Flow Complete")

    [if !supportLists]15. [endif]            }.catch { error ->

    [if !supportLists]16. [endif]                Log.d("xys", "Flow Error $error")

    [if !supportLists]17. [endif]            }.collect {

    [if !supportLists]18. [endif]                Log.d("xys", "Result---$it")

    [if !supportLists]19. [endif]            }

    [if !supportLists]20. [endif]        }

    也可以写在onCompletion中

    [if !supportLists]1. [endif]lifecycleScope.launch {

    [if !supportLists]2. [endif]            flow {

    [if !supportLists]3. [endif]                for (i in 0..3) {

    [if !supportLists]4. [endif]                    emit(i.toString())

    [if !supportLists]5. [endif]                }

    [if !supportLists]6. [endif]                throw Exception("Test")

    [if !supportLists]7. [endif]            }.retryWhen { _, retryCount ->

    [if !supportLists]8. [endif]                retryCount <= 3

    [if !supportLists]9. [endif]            }.onStart {

    [if !supportLists]10. [endif]                Log.d("xys", "Start Flow in ${Thread.currentThread().name}")

    [if !supportLists]11. [endif]            }.onEach {

    [if !supportLists]12. [endif]                Log.d("xys", "emit value---$it")

    [if !supportLists]13. [endif]            }.onCompletion {e->

    [if !supportLists]14. [endif]                if(e!=null){

    [if !supportLists]15. [endif]                    Log.d("xys", "Flow Exception ${e}")

    [if !supportLists]16. [endif]                }else{

    [if !supportLists]17. [endif]                    Log.d("xys", "Flow Complete")

    [if !supportLists]18. [endif]                }

    [if !supportLists]19. [endif]            }.collect {

    [if !supportLists]20. [endif]                Log.d("xys", "Result---$it")

    [if !supportLists]21. [endif]            }

    [if !supportLists]22. [endif]        }

    在我们了解了一下简单的常用用法之后,flow对于我们已经变得没有那么陌生了,我们来真正重新认识一下flow

    重识Flow

    3.1什么是冷流热流

    冷流当数据被订阅的时候,发布者才开始执行发射数据流的代码。并且当有多个订阅者的时候,每一个订阅者何发布者都是一对一的关系,每个订阅者都会收到发布者完整的数据。热流无论有没有订阅者订阅,事件始终都会发生。当热流有多个订阅者时,发布者跟订阅者是一对多的关系,热流可以与多个订阅者共享信息。

    示例:

    [if !supportLists]1. [endif]fun simpleFlow2() = flow<Int> {

    [if !supportLists]2. [endif]        println("Flow started")

    [if !supportLists]3. [endif]        for (i in 1..3) {

    [if !supportLists]4. [endif]            delay(1000)

    [if !supportLists]5. [endif]            emit(i)

    [if !supportLists]6. [endif]        }

    [if !supportLists]7. [endif]

    [if !supportLists]8. [endif]

    [if !supportLists]9. [endif]    }

    [if !supportLists]10. [endif]

    [if !supportLists]11. [endif]    fun testCold(){

    [if !supportLists]12. [endif]

    [if !supportLists]13. [endif]        viewModelScope.launch {

    [if !supportLists]14. [endif]            val flow = simpleFlow2()

    [if !supportLists]15. [endif]            println("Calling collect...")

    [if !supportLists]16. [endif]            flow.collect { value -> println(value) }

    [if !supportLists]17. [endif]

    [if !supportLists]18. [endif]            println("Calling collect again...")

    [if !supportLists]19. [endif]            flow.collect { value -> println(value) }

    [if !supportLists]20. [endif]        }

    [if !supportLists]21. [endif]    }

    [if !supportLists]22. [endif]    val _events = MutableSharedFlow<String>()

    [if !supportLists]23. [endif]

    [if !supportLists]24. [endif]    //这里会每隔1s发送一个数据

    [if !supportLists]25. [endif]    suspend fun foo1(){

    [if !supportLists]26. [endif]        Log.i(TAG, "foo: 开始发送数据")

    [if !supportLists]27. [endif]        Log.i(TAG, "foo: 开始发送A")

    [if !supportLists]28. [endif]        _events.emit("A")

    [if !supportLists]29. [endif]        Log.i(TAG, "foo: 结束发送A")

    [if !supportLists]30. [endif]        delay(1000)

    [if !supportLists]31. [endif]        Log.i(TAG, "foo: 开始发送B")

    [if !supportLists]32. [endif]        _events.emit("B")

    [if !supportLists]33. [endif]        Log.i(TAG, "foo: 结束发送B")

    [if !supportLists]34. [endif]        delay(1000)

    [if !supportLists]35. [endif]        Log.i(TAG, "foo: 开始发送C")

    [if !supportLists]36. [endif]        _events.emit("C")

    [if !supportLists]37. [endif]        Log.i(TAG, "foo: 结束发送C")

    [if !supportLists]38. [endif]        Log.i(TAG, "foo: 结束发送数据")

    [if !supportLists]39. [endif]    }

    [if !supportLists]40. [endif]

    [if !supportLists]41. [endif]    fun testHot(){

    [if !supportLists]42. [endif]        //先开启协程,创建出SharedFlow

    [if !supportLists]43. [endif]        viewModelScope.launch {

    [if !supportLists]44. [endif]            foo1()

    [if !supportLists]45. [endif]        }

    [if !supportLists]46. [endif]        //立马进行收集

    [if !supportLists]47. [endif]        viewModelScope.launch(Dispatchers.IO) {

    [if !supportLists]48. [endif]            _events.collect {

    [if !supportLists]49. [endif]                Log.i(TAG, "initData: A开始收集 $it")

    [if !supportLists]50. [endif]            }

    [if !supportLists]51. [endif]        }

    [if !supportLists]52. [endif]        //延迟2秒再进行收集

    [if !supportLists]53. [endif]        viewModelScope.launch {

    [if !supportLists]54. [endif]            delay(2000)

    [if !supportLists]55. [endif]            _events.collect {

    [if !supportLists]56. [endif]                Log.i(TAG, "initData: B开始收集 $it")

    [if !supportLists]57. [endif]            }

    [if !supportLists]58. [endif]        }

    [if !supportLists]59. [endif]    }

    从控制台可以观察到打印的信息:

    foo: 开始发送数据

    foo: 开始发送A

    foo: 结束发送A

    foo: 开始发送B

    initData: A开始收集 B

    foo: 结束发送B

    foo: 开始发送C

    initData: A开始收集 C

    initData: B开始收集 C

    foo: 结束发送C

    foo: 结束发送数据

    热流之一SharedFlow

    构造函数:

    public fun <T> MutableSharedFlow(

        replay: Int = 0,

        extraBufferCapacity: Int = 0,

        onBufferOverflow: BufferOverflow = BufferOverflow.SUSPEND

    ): MutableSharedFlow<T>

    replay:表示当有新的订阅者collect时,发送几个已经发送过的数据给它,默认是0,即默认新订阅者不会获取到订阅之前的数据。

    -extraBufferCapacity:表示减去replay,这个Flow还可以缓存多少个数据,默认是0;

    -onBufferOverflow:表示缓存策略,即缓冲区满了后,如何处理,默认是挂起。z

    热流之二喜闻乐见的StateFlow

    官方就是希望用这个来替代LiveData,那么它到底是如何来替代LiveData的呢?

    1)、特性

    StateFlow是SharedFlow的子类,根据前面说的那么它是个热流,可以被多个观察者观察,同时可以设置它消失的scope以及条件。StateFlow只更新最新的数据,也就是它是一个replay为0的SharedFlow。

    StateFlow里面和LiveData很像,都有个value来保存其值,也可以通过这个属性来获取或者设置它的值。

    2)、使用

    使用MutableStateFlow就和LiveData一样,不过它需要一个默认值。

    也可以使用stateIn函数把一个Flow转成StateFlow,直接看这个函数:

    val result = userId.mapLatest { newUserId ->

        repository.observeItem(newUserId)

    }.stateIn(

        scope = viewModelScope,

        started = WhileSubscribed(5000),

        initialValue = Result.Loading

    )

    从这个最基本的使用,我们可以看出以下信息:

    它的范围是viewModelScope,当viewModelScope结束时,流会停止。

    当观察者都被移除后,超过5s,为了不浪费性能,流会停止。

    会有个默认值,也就是这个initivalValue。

    3)、观察

    从上面来看,确实很像LiveData,不过LiveData有个很重要的功能就是具有生命周期感知性能力,在UI处于活跃状态时才会更新数据,那这个StateFlow在收集时并没有传递lifecycleOwner,那如何达到一样的效果呢?

    直接推荐最佳写法:

    onCreateView(...) {

        viewLifecycleOwner.lifecycleScope.launch {

            viewLifecycleOwner.lifecycle.repeatOnLifecycle(STARTED) {

                myViewModel.myUiState.collect { ... }

            }

        }

    }

    解释一下为什么是这样:

    如果我们直接使用launch的话,可能会在onStop执行时这时来个数据更新,我的View根本没法更新,所以会造成错误,这里要达到和LiveData一样的效果在界面活跃时进行更新,所以这里启动协程就需要使用launchWhenStarted。

    这个看起来没啥问题,不过我们前面不是有个WhileSubscribed这个优化项呢,表示没有观察者时5s后停止上游数据发射,那现在这个则无法做到,这时就需要使用repeatOnLifecycle

    这个repeatOnLifecycle的作用就是当满足特定状态时启动协程,并且在生命周期退出这个状态时停止协程。那这就可以比如我APP退到后台,这时如果超过5S,则不会再订阅,协程终止,这时上游发射逻辑也会停止,提高性能。当APP再切换到前台时,会执行onStart,这时又会有观察者,这时上游逻辑又会开始,

    3.2 flow的操作符

    通过之前与rxjava对比,已经熟悉了一些的操作符。接下来再看一下还有哪些常用的操作符。

    3.2.1创建操作符

    Flow,flowOf,asFlow

    3.2.2末端操作符

    是在流上用于启动流收集的挂起函数。collect是最基础的末端操作符,但是还有另外一些更方便使用的末端操作符:

    Collect,collectIndexed,collectLatest

    toCollection、toSet、toList

    这些操作符用于将Flow转换为Collection、Set和List。

    launchIn

    在指定的协程作用域中直接执行Flow。

    last、lastOrNull、first、firstOrNull

    3.2.3状态操作符

    状态操作符不做任何修改,只是在合适的节点返回状态。

    onStart:在上游生产数据前调用

    onCompletion:在流完成或者取消时调用

    onEach:在上游每次emit前调用

    onEmpty:流中未产生任何数据时调用

    catch:对上游中的异常进行捕获

    retry、retryWhen:在发生异常时进行重试,retryWhen中可以拿到异常和当前重试的次数

    3.2.4转换操作符

    map、mapLatest、mapNotNull

    3.2.5过滤操作符

    filter、filterInstance、filterNot、filterNotNull

    3.2.6组合操作符

    combine、combineTransform

    Merge、zip

    3.2.7其他

    [if !supportLists]四.[endif]Flow的应用

    [if !supportLists]1.[endif]使用flow实现倒计时

    [if !supportLists]1. [endif]fun countDownCoroutines(

    [if !supportLists]2. [endif]        total: Int,

    [if !supportLists]3. [endif]        scope: CoroutineScope,

    [if !supportLists]4. [endif]        onTick: (Int) -> Unit,

    [if !supportLists]5. [endif]        onStart: (() -> Unit)? = null,

    [if !supportLists]6. [endif]        onFinish: (() -> Unit)? = null,

    [if !supportLists]7. [endif]    ): Job {

    [if !supportLists]8. [endif]        return flow {

    [if !supportLists]9. [endif]            for (i in total downTo 0) {

    [if !supportLists]10. [endif]                emit(i)

    [if !supportLists]11. [endif]                delay(1000)

    [if !supportLists]12. [endif]            }

    [if !supportLists]13. [endif]        }.flowOn(Dispatchers.Main)

    [if !supportLists]14. [endif]            .onStart { onStart?.invoke() }

    [if !supportLists]15. [endif]            .onCompletion { onFinish?.invoke() }

    [if !supportLists]16. [endif]            .onEach { onTick.invoke(it) }

    [if !supportLists]17. [endif]            .launchIn(scope)

    [if !supportLists]18. [endif]    }

    [if !supportLists]1. [endif]countDownCoroutines(5, lifecycleScope,

    [if !supportLists]2. [endif]            onTick = { second ->

    [if !supportLists]3. [endif]                tvSkip.text = "${second}s跳过"

    [if !supportLists]4. [endif]            }, onStart = {

    [if !supportLists]5. [endif]                // 倒计时开始

    [if !supportLists]6. [endif]                if (show) {

    [if !supportLists]7. [endif]                    tvSkip.visibility = View.VISIBLE

    [if !supportLists]8. [endif]                }

    [if !supportLists]9. [endif]            }, onFinish = {

    [if !supportLists]10. [endif]                // 倒计时结束,重置状态

    [if !supportLists]11. [endif]                if (show) {

    [if !supportLists]12. [endif]                    enterHomePage()

    [if !supportLists]13. [endif]                }

    [if !supportLists]14. [endif]            })

    [if !supportLists]2.[endif]flow做搜索限流

    [if !supportLists]1. [endif]lifecycleScope.launchWhenCreated {

    [if !supportLists]2. [endif]            _etState.

    [if !supportLists]3. [endif]            sample(500).filter {

    [if !supportLists]4. [endif]                it.isNotEmpty()

    [if !supportLists]5. [endif]            }.collect {

    [if !supportLists]6. [endif]                viewModel.searchArticles(it)

    [if !supportLists]7. [endif]            }

    [if !supportLists]8. [endif]        }

    [if !supportLists]3.[endif]flow结合room

    [if !supportLists]1. [endif]@Dao

    [if !supportLists]2. [endif]interface UserDao {

    [if !supportLists]3. [endif]

    [if !supportLists]4. [endif]    //返回插入行 ID 的Insert DAO 方法永远不会返回 -1,因为即使存在冲突,此策略也将始终插入行

    [if !supportLists]5. [endif]    @Insert(onConflict = OnConflictStrategy.REPLACE)

    [if !supportLists]6. [endif]    suspend fun insert(user: User)

    [if !supportLists]7. [endif]

    [if !supportLists]8. [endif]    @Query("SELECT * FROM user")

    [if !supportLists]9. [endif]    fun getAll(): Flow<List<User>>

    [if !supportLists]10. [endif]

    [if !supportLists]11. [endif]}

    [if !supportLists]1. [endif] fun getAll(): Flow<List<User>> {

    [if !supportLists]2. [endif]        return AppDatabase.getInstance(getApplication())

    [if !supportLists]3. [endif]            .userDao()

    [if !supportLists]4. [endif]            .getAll()

    [if !supportLists]5. [endif]            .catch { e -> e.printStackTrace() }

    [if !supportLists]6. [endif]            .flowOn(Dispatchers.IO) //切换上下文为IO异步

    [if !supportLists]7. [endif]    }

    [if !supportLists]4.[endif]flow结合Retrofit

    [if !supportLists]1. [endif] viewModelScope.launch {

    [if !supportLists]2. [endif]            flow {

    [if !supportLists]3. [endif]                //这里就是通过Retrofit从服务器拿到对应key过滤后的文章内容

    [if !supportLists]4. [endif]                val map = mapOf("word" to key, "search_match_type" to 1.toString(), "page_size" to 30)

    [if !supportLists]5. [endif]                val list = RetrofitClient.articleApi.getSearchResult(map as Map<String, String>)

    [if !supportLists]6. [endif]                //将对应数据发射出去

    [if !supportLists]7. [endif]                emit(list)

    [if !supportLists]8. [endif]            }.flowOn(Dispatchers.IO)

    [if !supportLists]9. [endif]                .catch {

    [if !supportLists]10. [endif]                        e -> e.printStackTrace()

    [if !supportLists]11. [endif]                }

    [if !supportLists]12. [endif]                .collect {

    [if !supportLists]13. [endif]                    //这里收到对应数据,更新对应的LiveData数据

    [if !supportLists]14. [endif]                    articles.setValue(it.article?.items)

    [if !supportLists]15. [endif]                }

    [if !supportLists]16. [endif]        }

    代码已经托管到github

    git@github.com:mm46468648/flowTest.git

    补充小知识:

    一.Navigation+BottomNavigationView的用法

    [if !supportLists]1.[endif]创建nav_graph

    [if !supportLists]2.[endif]创建bottom_nav_menu

    [if !supportLists]3.[endif]布局

    [if !supportLists]1. [endif]<fragment

    [if !supportLists]2. [endif]        android:id="@+id/fragment"

    [if !supportLists]3. [endif]        android:name="androidx.navigation.fragment.NavHostFragment"

    [if !supportLists]4. [endif]        android:layout_width="match_parent"

    [if !supportLists]5. [endif]        android:layout_height="match_parent"

    [if !supportLists]6. [endif]        app:defaultNavHost="true"

    [if !supportLists]7. [endif]        app:layout_constraintBottom_toBottomOf="parent"

    [if !supportLists]8. [endif]        app:layout_constraintLeft_toLeftOf="parent"

    [if !supportLists]9. [endif]        app:layout_constraintRight_toRightOf="parent"

    [if !supportLists]10. [endif]        app:layout_constraintTop_toTopOf="parent"

    [if !supportLists]11. [endif]        app:navGraph="@navigation/nav_graph" />

    [if !supportLists]12. [endif]

    [if !supportLists]13. [endif]

    [if !supportLists]14. [endif]    <com.google.android.material.bottomnavigation.BottomNavigationView

    [if !supportLists]15. [endif]        android:id="@+id/bottom_nav_view"

    [if !supportLists]16. [endif]        android:layout_width="match_parent"

    [if !supportLists]17. [endif]        android:layout_height="wrap_content"

    [if !supportLists]18. [endif]        app:layout_constraintBottom_toBottomOf="parent"

    [if !supportLists]19. [endif]        app:layout_constraintLeft_toLeftOf="parent"

    [if !supportLists]20. [endif]        app:layout_constraintRight_toRightOf="parent"

    [if !supportLists]21. [endif]        app:menu="@menu/bottom_nav_menu" />

    [if !supportLists]4.[endif]关联:

    [if !supportLists]1. [endif] //fragment的容器视图,navHost的默认实现——NavHostFragment

    [if !supportLists]2. [endif]val navHostFragment = supportFragmentManager.findFragmentById(R.id.fragment) as NavHostFragment

    [if !supportLists]3. [endif]

    [if !supportLists]4. [endif] //管理应用导航的对象

    [if !supportLists]5. [endif]val navController = navHostFragment.navController

    [if !supportLists]6. [endif]

    [if !supportLists]7. [endif]//fragment与BottomNavigationView的交互交给NavigationUI

    [if !supportLists]8. [endif]bottom_nav_view.setupWithNavController(navController)

    注意:

    menu中的id和nav_garph中的id一定要对应,否则会找不到需要展示的fragment

    参考:

    https://juejin.cn/post/6898234461361307655/

    相关文章

      网友评论

          本文标题:协程之Flow

          本文链接:https://www.haomeiwen.com/subject/qpoifrtx.html