Android开发之MVVM模式实践(六)
我们可以事先定义一些事先常见的网络错误,方便后续使用。// 未知错误// 网络连接错误// 连接超时// 错误的请求// 数据解析错误// 取消请求。
我们可以事先定义一些事先常见的网络错误,方便后续使用。
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码与MessageError:继承于ParseResult,网络请求异常,未成功,持有异常信息
在ParseResult中do开头的函数都是设置对应处理的代码块,另外有个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()
}
}
此处的ApiClient是BaseHttpClient的子类即对Retrofit+OkHttp的封装,并做了单例处理,整个请求流程呈现链式结构。虽然doSuccess、doFailure以及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用于获取文章详情,最后打印的结果为:
开始
第一次
第二次
结束
可以看出,完全是顺序执行。
请求并发
fun get_info() {
launchOnUI {
val listAsync = async {
var articles = ArrayList
ApiClient.getInstance()
.requestSafely(FlyInterface::class.java) {
it.get_article_list(20)
}.doSuccess {
articles = it!!.results
}
.procceed()
return@async articles
}
val detailAsync = async {
var article: Article? = null
ApiClient.getInstance()
.requestSafely(FlyInterface::class.java) {
it.get_article(2)
}.doSuccess {
article = it
}
.procceed()
return@async article!!
}
val articles = listAsync.await()
val articleDetail = detailAsync.await()
}
}
使用async实现并发,同时请求文章列表和文章详情,并获取对应的值。
三、老项目使用协程
协程很香,这毋庸置疑,但是对于一些已经使用了回调形式的网络请求的老项目来说,将所有的网络请求改为上述的协程形式是不现实的,而如果既想不改动原来的回调形式,又想使用协程,有没有办法呢?当然是有的!
首先我们看下回调形式下的网络请求:
HttpClient.getInstance().addGetDataCallback(url:String, object :SimpleAppGetCallback(){
override fun onSuccess(data: T?) {
}
override fun onFailure(code: Int, msg: String) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。






既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
新的开始
改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。
《系列学习视频》
《系列学习文档》

《我的大厂面试之旅》

]
新的开始
改变人生,没有什么捷径可言,这条路需要自己亲自去走一走,只有深入思考,不断反思总结,保持学习的热情,一步一步构建自己完整的知识体系,才是最终的制胜之道,也是程序员应该承担的使命。
《系列学习视频》
[外链图片转存中…(img-vyDj2U7g-1711919461628)]
《系列学习文档》
[外链图片转存中…(img-TYYpMnah-1711919461629)]
《我的大厂面试之旅》
[外链图片转存中…(img-MzrdCszs-1711919461630)]
更多推荐


所有评论(0)