背景

移动app中展示的数据多数都是通过服务器接口获取的,当接口数据与用户相关时,服务端接口会要求客户端把用户信息通过接口发送到服务器。普遍的做法是把用户登录后的token数据发送给服务器的接口。考虑到安全问题,token都有过期时间,token过期后服务端就不能通过这个token查询用户的具体信息了。为了刷新过期token,服务端会提供一个刷新token的接口给客户端使用。

问题分析

由于要求上传token的服务端接口会有很多,所以这些接口的调用都需要考虑token过期失效问题。
这些接口调用的异常处理中需要增加token过期处理,在token过期的情况下触发token刷新处理。
token刷新后触发接口的重试请求。
如何使用flow实现
首先我们实现一个刷新token的flow。

private val refreshTokenFlow = flow {
        if (expiredToken) {
            cachedToken = serverApi.refreshToken("token-0")
            expiredToken = false
        }
        emit(cachedToken)
    }

expiredToken变量代表token是否过期,实际开发过程中这个变量的值应该根据过期时间计算得出的。cachedToken变量保存着最新的token值,请求用户相关信息的接口时可以把这个token传递给服务端用于查询。我们可以看到cachedToken的值是通过调用服务端提供的刷新接口获取的。这个flow最终发射的是最新的token值,同时我们也看到这个flow调用刷新接口的逻辑只有token过期时才会被调用。
第二步定义请求用户相关数据的flow

    private fun getDataFlow(token: String) = flow {
        emit(serverApi.getDataViaToken(token))
    }

由于请求用户数据的接口依赖token的值,所以这个flow是通过方法生成的。flow的生成也比较简单,它直接调用服务端的接口并将数据发射出去。

第三步将刷新token的flow和请求用户数据的flow展开

    private val userDataFlow = refreshTokenFlow.flatMapConcat{token->
        getDataFlow(token)
    }

flatMapConcat方法将前面定义的两个流拼接在一起,这时我们要是收集拼接后的userDataFlow,refreshTokenFlow会被收集,flatMapConcat方法接收到 refreshTokenFlow发射的token后开始收集getDataFlow方法返回的flow。这样连个flow的依赖关系通过flatMapConcat完美实现了。

第四步实现token过期重试机制

 private val userDataFlow = refreshTokenFlow.flatMapConcat{token->
     getDataFlow(token)
 }.retryWhen { cause, attempt ->
     if (attempt > 1) {
         false
     } else if(cause is InvalidTokenException) {
         expiredToken = true
         true
     }else{
         false
     }
 }.catch {
     msgView.text = it.message
 }

基于第三步的flow拼接我们添加了retryWhen和cach两个块。retryWhen块用于实现重试机制,参数cause是前面流程发生的异常,参数attempt代表重试的次数,返回值true代表进行重试,返回值false代表不进行重试。在retryWhen块中我们可以通过cause的类型来判断是否要重试,当cause为InvalidTokenException时代表token过期,所以进行重试并且重置了token过期expiredToken。为了避免无限地进行重试,这里限制重试次数为一次。catch块处理不进行重试时的逻辑,一般会将出错的信息显示到界面上。

总结

通过flow的方式实现token刷新机制相较于传统的方式有如下优势:

逻辑简单明了,刷新流程简单清晰直观。flow操作符语义简单明了,链式的方式声明处理过程更容易理解,没有复杂的嵌套。
通过声明的方式定义了刷新流程,同时也支持通过声明的方式扩展已有流程。比如这里拼接了刷新token的flow和获取用户数据的flow。
重试机制可以根据不同的依赖token接口进行定制处理,接口的重试机制更加灵活方便。这里没有针对重试流程定义新的flow,但在实际应用中我们可以为重试流程定义flow,然后将flow拼接在已有flow后。这样我们可以在不同的地方重用重试流程的flow。
我的公众号已经开通,公众号会同步发布。
欢迎关注我的公众号

————————————————
版权声明:本文为CSDN博主「mjlong123123」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/mjlong1...

03-05 15:56