Kotlin 协程 的实战,半路出家Android程序员看我轻松逆袭

} finally {
complete()
}
}
}

那我们的VIewModel中的getBanner方法这样写就好了:

fun getBanner() {
launch({
val result = repository.getBanner()
if (result.errorCode == 0) {
LogUtils.d(result.data)
}
})

// 如果要处理error,如下
/launch({
val result = repository.getBanner()
if (result.errorCode == 0) {LogUtils.d(result.data)}
}, {
//处理error
LogUtils.d(it.message)
})
/
}

又有小伙伴说了,那我想把code不等于0的时候全抛出错误,统一处理怎么办? 那我们就再封装一下,在BaseView中加入: 我们把统一异常处理先抽出来:

/**

  • 异常统一处理
    */
    private suspend fun handleException(
    block: suspend CoroutineScope.() -> BaseResult,
    success: suspend CoroutineScope.(BaseResult) -> Unit,
    error: suspend CoroutineScope.(ResponseThrowable) -> Unit,
    complete: suspend CoroutineScope.() -> Unit
    ) {
    coroutineScope {
    try {
    success(block())
    } catch (e: Throwable) {
    error(ExceptionHandle.handleException(e))
    } finally {
    complete()
    }
    }
    }

然后再写一个 executeResponse 方法来过滤:

/**

  • 请求结果过滤
    */
    private suspend fun executeResponse(
    response: BaseResult,
    success: suspend CoroutineScope.(T) -> Unit
    ) {
    coroutineScope {
    if (response.errorCode == 0 ) success(response.data)
    else throw ResponseThrowable(response.errorCode, response.errorMsg)
    }
    }

最后我们再写一个 launchOnlyresult 方法把他们结合起来:

fun launchOnlyresult(
block: suspend CoroutineScope.() -> BaseResult,
success: (T) -> Unit,
error: (ResponseThrowable) -> Unit = { },
complete: () -> Unit = {}
) {
launchUI {
handleException(
{ withContext(Dispatchers.IO) { block() } },
{ res ->
executeResponse(res) { success(it) }
},
{
error(it)
},
{
complete()
}
)
}
}

异常类的代码就不贴了,没什么好说的,末尾会给Demo地址,在里面看吧,现在我们获取Banner数据就变成这样了:

fun getBanner() {
launchOnlyresult({ repository.getBanner() }, {
LogUtils.d(it) // it是Banner 数据
})
// 处理Error
/launchOnlyresult({ repository.getBanner() }, {
mBanners.value = it
},{
LogUtils.d(it.errMsg)
})
/
}

我们的一个请求已经可以简单成这个样子了,相比于用RxJava的方式是不是更舒服呢。说到这里有的兄弟可能就说了,单个网络请求确实很简单,但是如果多个呢?还有些请求要依赖其他请求的结果呢?我们在业务逻辑越来越复杂,RxJava有多种操作符来使用,你这个要怎么搞?
接下来另一个人物要登场了,带着这些问题我们再来说下协程的另一个东西 Flow 异步流

3.2 Flow

带着上面的问题我们看下Flow 能干什么,看着名字可能有些陌生,但是我们了解之后肯定又会非常熟悉。他翻译成中文是 意思,我们在协程中,做异步可以返回一个值,当我们想返回多个值的时候,Flow就开始展现他的作用了,我们看下具体使用场景: 我们看玩安卓的 导航数据项目列表数据 两个接口,获取项目列表的时候需要依赖导航数据接口里边的 id,我们来用Flow实现 首先是Servie:

/**

  • 导航数据
    */
    @GET(“project/tree/json”)
    suspend fun naviJson(): BaseResult<List>

/**

  • 项目列表
  • @param page 页码,从0开始
    */
    @GET(“project/list/{page}/json”)
    suspend fun getProjectList(@Path(“page”) page: Int, @Query(“cid”) cid: Int): BaseResult

ViewModel中的实现:

@ExperimentalCoroutinesApi
@FlowPreview
fun getFirstData() {
launchUI {
flow { emit(repository.getNaviJson()) }
.flatMapConcat {
return@flatMapConcat if (it.isSuccess()) {
// 业务操作 …
// LogUtils.d(it) // it 是BaseResult<List>
// …
flow { emit(repository.getProjectList(page, it.data[0].id)) }
} else throw ResponseThrowable(it.errorCode, it.errorMsg)
}.onStart{
// 会在 emit 发射之前调用
}
.flowOn(Dispatchers.IO) // 这个是指烦气发射的所在协程
.onCompletion {
// 流执行完毕会调用
}
.catch {
// 遇到错误时会调用
}
.collect {
// 收集 ,FLow只有在我们
LogUtils.d(it) // it 是BaseResult
}

}
}

有的兄弟可能看到上边代码会说,似曾相识啊,没错跟RxJava是一个思想,Flow只能运行在协程中,上边的代码优化过后:是这个样子的:

@ExperimentalCoroutinesApi
@FlowPreview
fun getFirstData() {
launchUI {
launchFlow { repository.getNaviJson() }
.flatMapConcat {
return@flatMapConcat if (it.isSuccess()) {
navData.addAll(it.data)
it.data.forEach { item -> navTitle.add(item.name) }
launchFlow { repository.getProjectList(page, it.data[0].id) }
} else throw ResponseThrowable(it.errorCode, it.errorMsg)
}
.onStart { defUI.showDialog.postValue(null) }
.flowOn(Dispatchers.IO)
.onCompletion { defUI.dismissDialog.call() }
.catch {
// 错误处理
val err = ExceptionHandle.handleException(it)
LogUtils.d(“${err.code}: ${err.errMsg}”)
}
.collect {
if (it.isSuccess()) items.addAll(it.data.datas)
}
}

}

Demo中使用了LiveData 更新数据,如果把所有东西都贴出来实在有点多,只放了部分代码。来简单说下这些操作符的作用吧:

  • flow:构建器,他可以发射数据多个数据,用**emit()**来发射
  • flatMapConcat :这个是在一个流收集完成之后,再收集下一个流
  • onStart:这个看名字估计也能猜出来,就是在发射之前做一些事情,我们可以在这里再 emit()一个数据,他会在flow里边的数据发射之前发射,我们上边的例子,是在OnStart里边打开了等待框
  • flowOn:这个就是指定我们的流运行在那个协程里边,我们指定的是 Dispatchers.IO
  • onCompletion :是在所有流都收集完成了,就会触发,我们可以在这里取消等待框再合适不过了
  • catch:这个就是遇到错误的时候会触发,我们我错误处理就是在这里来做了
  • collect:这个就是收集器的意思,我们的结果都在这里来处理。也只有我们调用了这个收集方法,数据才真正的开始发射了,这也是官方说的一句话,流是冷的,就是这个意思

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

面试宝典

面试必问知识点、BATJ历年历年面试真题+解析

学习经验总结

(一)调整好心态
心态是一个人能否成功的关键,如果不调整好自己的心态,是很难静下心来学习的,尤其是现在这么浮躁的社会,大部分的程序员的现状就是三点一线,感觉很累,一些大龄的程序员更多的会感到焦虑,而且随着年龄的增长,这种焦虑感会越来越强烈,那么唯一的解决办法就是调整好自己的心态,要做到自信、年轻、勤奋。这样的调整,一方面对自己学习有帮助,另一方面让自己应对面试更从容,更顺利。

(二)时间挤一挤,制定好计划
一旦下定决心要提升自己,那么再忙的情况下也要每天挤一挤时间,切记不可“两天打渔三天晒网”。另外,制定好学习计划也是很有必要的,有逻辑有条理的复习,先查漏补缺,然后再系统复习,这样才能够做到事半功倍,效果才会立竿见影。

(三)不断学习技术知识,更新自己的知识储备
对于一名程序员来说,技术知识方面是非常重要的,可以说是重中之重。**要面试大厂,自己的知识储备一定要非常丰富,若缺胳膊少腿,别说在实际工作当中,光是面试这一关就过不了。**对于技术方面,首先基础知识一定要扎实,包括自己方向的语言基础、计算机基础、算法以及编程等等。

,切记不可“两天打渔三天晒网”。另外,制定好学习计划也是很有必要的,有逻辑有条理的复习,先查漏补缺,然后再系统复习,这样才能够做到事半功倍,效果才会立竿见影。

(三)不断学习技术知识,更新自己的知识储备
对于一名程序员来说,技术知识方面是非常重要的,可以说是重中之重。**要面试大厂,自己的知识储备一定要非常丰富,若缺胳膊少腿,别说在实际工作当中,光是面试这一关就过不了。**对于技术方面,首先基础知识一定要扎实,包括自己方向的语言基础、计算机基础、算法以及编程等等。

本文已被CODING开源项目:《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》收录

  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值