广告
返回顶部
首页 > 资讯 > 移动开发 >Kotlin协程之Flow异常示例处理
  • 797
分享到

Kotlin协程之Flow异常示例处理

2024-04-02 19:04:59 797人浏览 薄情痞子
摘要

目录示例一.catch方法catchImpl方法二. onCompletion方法1.unsafeFlow方法2.ThrowinGCollector类三. retryWhen方法示例

示例

代码如下:

launch(Dispatchers.Main) {
    // 第一部分
    flow {
        emit(1)
        throw NullPointerException("e")
    }.catch {
        Log.d("liduo", "onCreate1: $it")
    }.collect {
        Log.d("liudo", "onCreate2: $it")
    }
    // 第二部分
    flow {
        emit(1)
    }.onCompletion {
        Log.d("liduo", "onCreate3: $it")
    }.collect {
        Log.d("liudo", "onCreate4: $it")
    }
    // 第三部分
    flow {
        emit(1)
        throw NullPointerException("e")
    }.retryWhen { cause, attempt ->
        cause !is NullPointerException && attempt <= 2
    }.collect {
        Log.d("liudo", "onCreate5: $it")
    }
}

一.catch方法

catch方法用于捕获上游流产生的异常,代码如下:

public fun <T> Flow<T>.catch(action: suspend FlowCollector<T>.(cause: Throwable) -> Unit): Flow<T> =
    flow { // 创建Flow对象
        // 触发上游流的执行,并捕获异常
        val exception = catchImpl(this)
        // 捕获到异常,则回调action处理
        if (exception != null) action(exception)
    }

catch方法是Flow接口的扩展方法,并返回一个Flow类型的对象。在catch方法中,调用flow方法创建了一个Flow对象。

catch方法核心是通过catchImpl方法实现异常的捕获,如果成功捕获到异常,则回调参数action处理。这里参数action是FlowCollector接口的扩展方法,因此可以继续调用emit方法,向下游发送值。

catchImpl方法

当下游调用collect方法时,会触发catch方法创建的Flow对象的执行,并调用catchImpl方法来处理,代码如下:

internal suspend fun <T> Flow<T>.catchImpl(
    collector: FlowCollector<T>
): Throwable? {
    // 保存下游流执行抛出的异常
    var fromDownstream: Throwable? = null
    try {
        // 触发上游流的执行
        collect {
            try {
                // 将上游流发送的值作为参数,触发下游流执行
                collector.emit(it)
            } catch (e: Throwable) { // 如果下游流在执行中发生异常,保存并抛出
                fromDownstream = e
                throw e
            }
        }
    } catch (e: Throwable) { // 这里捕获的异常,可能为上游流的异常——collect方法,
                             // 也可能为下游流的异常——emit方法
        // 如果异常是下游流产生的异常,或者是协程取消时抛出的异常
        if (e.isSameExceptionAs(fromDownstream) || e.isCancellationCause(coroutineContext)) {
            throw e // 再次抛出,交给下游处理
        } else { // 如果是上游流的异常且不为协程取消异常
            return e // 成功捕获
        }
    }
    // 未捕获到异常,返回
    return null
}

catchImpl方法是Flow接口的扩展方法,因此在调用collect方法时,会触发上游流的执行。catchImpl方法的核心在于:将上游发出的值传递给下游处理,并对这一过程进行了异常捕获操作。

二. onCompletion方法

onCompletion方法用于在上游的流全部执行完毕后最后执行,代码如下:

public fun <T> Flow<T>.onCompletion(
    action: suspend FlowCollector<T>.(cause: Throwable?) -> Unit
): Flow<T> = unsafeFlow { // 创建一个Flow对象
    try {
        // 触发上游流的执行
        // this表示下游的FlowCollector
        collect(this)
    } catch (e: Throwable) {// 如果下游发生异常
        // 将异常封装成ThrowingCollector类型的FlowCollector,并回调参数action,
        ThrowingCollector(e).invokeSafely(action, e)
        // 抛出异常
        throw e
    }
    // 如果正常执行结束,会走到这里
    val sc = SafeCollector(this, currentCoroutineContext())
    try {
        // 回调执行参数action
        sc.action(null)
    } finally {
        sc.releaseIntercepted()
    }
}

onCompletion方法是Flow接口的扩展方法,因此在调用collect方法时,会触发上游流的执行。同时,传入this作为参数,this表示下游流调用collect方法时,传给unsafeFlow方法创建的Flow对象的类型为FlowCollector的对象。onCompletion方法的核心在于:将自身创建的Flow对象作为上游与下游的连接容器,只有当流全部执行完毕或执行过程中发生异常,collect方法才可以执行完成,继续向下执行。

1.unsafeFlow方法

unsafeFlow方法用于创建一个类型为Flow对象,与之前在Kotlin协程:Flow基础原理提到过的SafeFlow类相比,unsafeFlow方法创建的Flow对象不会对执行的上下文进行检查,代码如下:

@Publishedapi
internal inline fun <T> unsafeFlow(@BuilderInference crossinline block: suspend FlowCollector<T>.() -> Unit): Flow<T> {
    // 返回一个匿名内部类
    return object : Flow<T> {
        // 回调collect方法是直接执行block
        override suspend fun collect(collector: FlowCollector<T>) {
            collector.block()
        }
    }
}

虽然onCompletion方法内部使用unsafeFlow方法创建Flow对象,但却使用了SafeCollector类。根据之前在Kotlin协程:Flow基础原理提到的,调用SafeCollector类的emit方法时,会对上下文进行检查。因此实际效果与使用SafeFlow类效果相同。

2.ThrowingCollector类

ThrowingCollector类也是一种FlowCollector,用于包裹异常。当调用它的emit方法时,会抛出包裹的异常,代码如下:

private class ThrowingCollector(private val e: Throwable) : FlowCollector<Any?> {
    override suspend fun emit(value: Any?) {
        // 抛出异常
        throw e
    }
}

为什么要重新创建ThrowingCollector对象,而不使用下游的FlowCollector对象呢?

为了防止当下游的流执行失败时,onCompletion方法的action参数执行时调用emit方法发送数据,这样会导致onCompletion方法作为在“finially代码块”使用时不是最后执行的方法。

onCompletion方法搭配与catch方法,实现try-catch-finially代码块的效果。

三. retryWhen方法

retryWhen方法与catch方法类似,都可以用于捕获上游流产生的异常。但两者不同之处在于,retryWhen方法还可以根据“异常类型”和“重试次数”来决定是否要再次触发上游流的执行,而且当retryWhen方法不打算再次触发上游流的执行时,捕获的异常会被抛出,代码如下:

// 参数cause表示捕获到的异常
// 参数attempt表示重试的次数
// 参数predicate返回true表示重新触发上游流的执行
public fun <T> Flow<T>.retryWhen(predicate: suspend FlowCollector<T>.(cause: Throwable, attempt: Long) -> Boolean): Flow<T> =
    // 创建一个Flow对象
    flow {
        // 记录重试次数
        var attempt = 0L
        // 表示是否重新触发
        var shallRetry: Boolean
        do {
            // 复位成false
            shallRetry = false
            // 触发上游流的执行,并捕获异常
            val cause = catchImpl(this)
            // 如果捕获到异常
            if (cause != null) {
                // 用户判断,是否要重新触发
                if (predicate(cause, attempt)) {
                    // 表示要重新触发
                    shallRetry = true
                    // 重试次数加1
                    attempt++
                } else { // 如果用户不需要重新触发
                    // 则抛出异常
                    throw cause
                }
            }
        // 判断是否重新触发
        } while (shallRetry)
    }

retryWhen方法是Flow接口的扩展方法。retryWhen方法的核心通过catchImpl方法实现对上游流的触发及异常捕获,并加入了由用户判断的重试逻辑实现。

到此这篇关于Kotlin协程之Flow异常示例处理的文章就介绍到这了,更多相关Kotlin Flow异常内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Kotlin协程之Flow异常示例处理

本文链接: https://www.lsjlt.com/news/168734.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Kotlin协程之Flow异常示例处理
    目录示例一.catch方法catchImpl方法二. onCompletion方法1.unsafeFlow方法2.ThrowingCollector类三. retryWhen方法示例...
    99+
    2022-11-13
  • Kotlin协程之Flow基础原理示例解析
    目录引言一.Flow的创建二.Flow的消费1.SafeFlow类2.AbstractFlow类3. SafeCollector类4.消费过程中的挂起引言 本文分析示例代码如下: l...
    99+
    2022-11-13
  • Kotlin协程之Flow触发与消费示例解析
    目录示例一.Flow的触发与消费1.onEach方法2.transform方法3.collect方法二.多消费过程的执行三.总结示例 代码如下: launch(Dispatchers...
    99+
    2022-11-13
  • kotlin协程上下文异常处理详解
    目录引言一、协程上下文1.CoroutineContext2.CorountineScope3.子协程继承父协程二、协程的异常传递1.协程的异常传播2.不同上下文(没有继承关系)之间...
    99+
    2022-11-13
  • kotlin协程异常处理的方法是什么
    Kotlin协程的异常处理方法有以下几种:1. 使用try/catch块:在协程内部使用try/catch块来捕获异常,并对异常进行...
    99+
    2023-09-20
    kotlin
  • Kotlin协程Dispatchers原理示例详解
    目录前置知识demostartCoroutineCancellableintercepted()函数DefaultScheduler中找dispatch函数Runnable传入Wor...
    99+
    2022-11-13
    Kotlin协程Dispatchers Kotlin Dispatchers
  • Java中trycatch处理异常示例
     描述说明: public class TryCatchStu {   实例代码: public static void main(String[] args) { Sy...
    99+
    2022-11-12
  • Java异常处理之陋习展播的示例分析
    这篇文章主要介绍了Java异常处理之陋习展播的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。1 OutputStreamWriter out = ...2 java....
    99+
    2023-06-03
  • android教程之hockeyapp捕获异常示例
    代码如下:package com.example.testhockeyapp;import net.hockeyapp.android.CrashManager;import...
    99+
    2022-06-06
    异常 示例 Android
  • SpringBoot异常处理之异常显示的页面问题
    目录导言实现步骤加入thymeleaf依赖然后创建controller使用X进行模糊匹配统一错误页面显示导言 默认情况下,SpringBoot 项目错误页面如下: 直接这样太丑了...
    99+
    2022-11-13
  • MySQL异常处理的示例分析
    这篇文章给大家分享的是有关MySQL异常处理的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。针对存储过程 、触发器或函数内部语句可能发生的错误或警告信息,需要进行相关异常...
    99+
    2022-10-18
  • React16中异常处理的示例分析
    这篇文章主要介绍React16中异常处理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!异常处理在 React 15.x 及之前的版本中,组件内的异常有可能会影响到 Reac...
    99+
    2022-10-19
  • Java中异常处理的示例分析
    这篇文章主要介绍Java中异常处理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!异常的定义在java中,异常就是java在编译、运行或运行过程中出现的错误总共有三种:1.编译错误 2.运行错误 3.逻辑错误...
    99+
    2023-06-25
  • SpringBoot实战之处理异常案例详解
    前段时间写了一篇关于实现统一响应信息的博文,根据文中实战操作,能够解决正常响应的一致性,但想要实现优雅响应,还需要优雅的处理异常响应,所以有了这篇内容。 作为后台服务,能够正确的处理...
    99+
    2022-11-12
  • Go语言异常处理的示例分析
    这篇文章主要为大家展示了“Go语言异常处理的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Go语言异常处理的示例分析”这篇文章吧。异常处理程序运行时,发生的不被期望的事件,它阻止了程序按...
    99+
    2023-06-20
  • java实现统一异常处理的示例
    对于Dao层 和Service产生的异常要一直网上抛,直至Controller层,但是对于controller层不能处理的异常也不能直接抛给前端。 为什么不能在service处理异常...
    99+
    2022-11-12
  • YII2全局异常处理的示例分析
    这篇文章主要介绍了YII2全局异常处理的示例分析,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。首先,我们必须理性认识到,任何一个即使稳定的系统中也是存在着大量的 bug,不管...
    99+
    2023-06-14
  • Python异常处理机制的示例分析
    这篇文章将为大家详细讲解有关Python异常处理机制的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。什么是异常?def num(a, b):   ...
    99+
    2023-06-22
  • Java异常处理机制的示例分析
    这篇文章给大家分享的是有关Java异常处理机制的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。1.初识异常我们在写代码的时候都或多或少碰到了大大小小的异常,例如:public class&nbs...
    99+
    2023-06-26
  • SpringMVC全局异常处理的示例分析
    这篇文章主要介绍SpringMVC全局异常处理的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!全局异常实现方式 Spring MVC 处理异常有 3 种方式1.使用 Spring MVC 提供的简单异常处理器...
    99+
    2023-06-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作