Android开发之MVVM模式实践(六),太现实了

Interface

interface FlyInterface {
/**

  • 获取文章列表
    */
    @GET(“article/”)
    suspend fun get_article_list(@Query(“page_size”) size: Int): ApiResponse<CommonListDto
    >
    }

interface的改造非常简单,仅仅是在函数前加上suspend修饰。

ApiResponse

abstract class HttpResponse(val code: Int, val msg: String, val data: T?) {
abstract fun isSuccess(): Boolean
}

class ApiResponse(code: Int, msg: String, data: T?) : HttpResponse(code, msg, data) {

override fun isSuccess(): Boolean {
return code == 0
}
}

ApiResponse是上述interface中函数的返回值,实现也非常简单。因为接口返回的数据格式一般都是统一的,例如:

{
“code”: 0;
“message”: “Success”;
“data”: {

}
}

所以,我们也需要将返回的数据格式用一个统一的数据模型来处理。

HttpError

我们可以事先定义一些事先常见的网络错误,方便后续使用。

enum class HttpError(val code: Int, @StringRes val message: Int) {
// 未知错误
UNKNOWN(-1, R.string.fly_http_error_unknow),

// 网络连接错误
CONNECT_ERROR(-2, R.string.fly_http_error_connect),

// 连接超时
CONNECT_TIMEOUT(-3, R.string.fly_http_error_connect_timeout),

// 错误的请求
BAD_NETWORK(-4, R.string.fly_http_error_bad_network),

// 数据解析错误
PARSE_ERROR(-5, R.string.fly_http_error_parse),

// 取消请求
CANCEL_REQUEST(-6, R.string.fly_http_cancel_request),
}

Retrofit

相信大部分同学在使用Retrofit时都会自己做二次封装的,此处就不附上详细的代码了,主要看关键代码,需要完整代码的可以去小益的Github上自行查看。

class BaseHttpClient {

/**

  • 获取service对象
  • @param service api所在的interface
    */
    fun getService(service: Class): T {
    var retrofitService: T? = serviceCache.get(service.canonicalName) as T
    if (retrofitService == null) {
    retrofitService = retrofitClient.create(service)
    serviceCache.put(service.canonicalName, retrofitService)
    }
    return retrofitService!!
    }

/**

  • 建议调用此方法发送网络请求

  • 因为协程中出现异常时,会直接抛出异常,所以使用try…catch方法捕获异常
    */
    suspend fun <T : Any, D : Any> requestSafely(
    apiInterface: Class,
    call: suspend (service: T) -> HttpResponse
    ): ParseResult {
    try {
    val s = getService(apiInterface)
    val response = call(s)
    return if (response.isSuccess()) {
    ParseResult.Success(response.data)
    } else {
    ParseResult.Failure(response.code, response.msg)
    }
    } catch (ex: Throwable) {
    return ParseResult.ERROR(ex, parseException(ex))
    }
    }

    }

  • getService:获取我们定义的interface

  • requestSafely:此方法中最值得注意的是try...catch,因为使用协程来进行网络请求时,如遇到问题会抛出异常,所以此处使用try...catch捕获。另外,此方法也对返回的Response做了简单的解析处理,并返回具体的ParseResult

ParseResult

sealed class ParseResult {
/* 请求成功,返回成功响应 */
data class Success(val data: T?) : ParseResult()

/* 请求成功,返回失败响应 */
data class Failure(val code: Int, var msg: String? = null) :
ParseResult()

/* 请求失败,抛出异常 */
data class ERROR(val ex: Throwable, val error: HttpError) : ParseResult()

private var successBlock: (suspend (data: T?) -> Unit)? = null
private var failureBlock: (suspend (code: Int, msg: String?) -> Unit)? = null
private var errorBlock: (suspend (ex: Throwable, error: HttpError) -> Unit)? = null
private var cancelBlock: (suspend () -> Unit)? = null

/**

  • 设置网络请求成功处理
    */
    fun doSuccess(successBlock: (suspend (data: T?) -> Unit)?): ParseResult {
    this.successBlock = successBlock
    return this
    }

/**

  • 设置网络请求失败处理
    */
    fun doFailure(failureBlock: (suspend (code: Int, msg: String?) -> Unit)?): ParseResult {
    this.failureBlock = failureBlock
    return this
    }

/**

  • 设置网络请求异常处理
    */
    fun doError(errorBlock: (suspend (ex: Throwable, error: HttpError) -> Unit)?): ParseResult {
    this.errorBlock = errorBlock
    return this
    }

/**

  • 设置网络请求取消处理
    */
    fun doCancel(cancelBlock: (suspend () -> Unit)?): ParseResult {
    this.cancelBlock = cancelBlock
    return this
    }

suspend fun procceed() {
when (this) {
is Success -> successBlock?.invoke(data)
is Failure -> failureBlock?.invoke(code, msg)
is ERROR -> {
if (this.error == HttpError.CANCEL_REQUEST) {
cancelBlock?.invoke()
} else {
errorBlock?.invoke(ex, error)
}
}
}
}
}

ParseResult是对HttpResponse解析后返回的类。ParseResult解析HttpResponse后出现三种返回:

  • Success:继承于ParseResult,网络请求成功并且返回的的Response状态也是成功,持有具体的Response数据
  • Failure:继承于ParseResult,网络请求成功但是返回的Response状态是失败,持有失败的Code码与Message
  • Error:继承于ParseResult,网络请求异常,未成功,持有异常信息

ParseResultdo开头的函数都是设置对应处理的代码块,另外有
procceed函数是真正执行响应处理。其中在对Error处理时分为了两种情况:

  • 一种是因为网络请求被取消产生的异常(经测试,网络请求取消会抛出取消异常)
  • 另一种是非网络请求取消产生的异常

因为网络请求取消从一定程度上来说不应该当作错误处理,所以要分开处理;防止项目中对异常错误进行了集中处理,比如弹出toast提示,此时如果用户取消了网络请求,也弹出一个网络请求取消的提示,这样的用户体验就比较糟糕了。

具体使用

fun get_article_list() {
launchOnUI {
ApiClient.getInstance()
.requestSafely(FlyInterface::class.java) {
it.get_article_list(20)
}.doSuccess {
articleList.value = it!!.results
}
.doFailure { code, msg -> showToast(msg ?: “获取文章列表失败”) }
.doError { ex, error -> showToast(error.message) }
.procceed()
}
}

此处的ApiClientBaseHttpClient的子类即对Retrofit+OkHttp的封装,并做了单例处理,整个请求流程呈现链式结构。虽然doSuccessdoFailure以及doError看上去有些像回调,但其实都是同步的。我们完全可以这么写:

fun get_article_info() {
launchOnUI {
println(">>>>>> 开始")
var articles = ArrayList

()
ApiClient.getInstance()
.requestSafely(FlyInterface::class.java) {
it.get_article_list(20)
}.doSuccess {
println(">>>>>> 第一次")
articles = it!!.results
}
.procceed()
ApiClient.getInstance()
.requestSafely(FlyInterface::class.java) {
it.get_article(articles[0].id)
}.doSuccess {
println(">>>>>> 第二次")
}
.procceed()
println(">>>>>> 结束")
}
}

先获取文章列表,再从文章列表中提取列表头部的文章ID用于获取文章详情,最后打印的结果为:

开始
第一次
第二次
结束

可以看出,完全是顺序执行。

.requestSafely(FlyInterface::class.java) {
it.get_article(articles[0].id)
}.doSuccess {
println(">>>>>> 第二次")
}
.procceed()
println(">>>>>> 结束")
}
}

先获取文章列表,再从文章列表中提取列表头部的文章ID用于获取文章详情,最后打印的结果为:

开始
第一次
第二次
结束

可以看出,完全是顺序执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值