Kotlin的魔能机甲——KtArmor网络调用封装(四)
前言
继上次分享KtArmor的基础使用方法, 在网络请求逻辑上,在调用上,总感觉不够优雅直观
,嵌套过深
的问题,这样使得代码看起来臃肿,不美观。所以在这篇中,分享一下我在网络请求调用
方面的 封装之路
。希望大家喜欢~
准备
在演示实例过程,我才用采用的是 玩Android 提供的接口 API。 框架方面,我采用的是 Retrofit
+ OkHttp
+ Coroutine
,示例演示是以 Kotlin
+ MVP
架构。如有不了解的同学,可以应当先去学习相关框架
,不然观看效果不佳。
阶段一
class LoginActivity : MvpActivity<LoginContract.Presenter>(), LoginContract.View {... 省略部分代码override fun initListener() {super.initListener()mBtnLogin.setOnClickListener {presenter.login(mEtAccount.str(), mEtPassword.str())}}... 省略部分代码
}
以上是部分节选代码。这里我以登录功能
为例,一般情况下在 LoginActivity 调用 Presenter 层发起 login
请求。
Presenter
class LoginPresenter(view: LoginContract.View) : BasePresenter<LoginContract.View>(view),LoginContract.Presenter {val presenterScope: CoroutineScope by lazy {CoroutineScope(Dispatchers.Main + Job())}override fun login(account: String, password: String) {... 省略部分代码// 启动一个 ui CoroutinepresenterScope.launch {tryCatch({view?.showLoading()val response = LoginModel.login(account, password)if (response.isSuccess()) {response.data?.let { view?.loginSuc(it) }} else {view?.loginFail(response.errorMsg)}}, {view?.loginError(it.toString())})}... 省略部分代码}
}
Model
object LoginModel : BaseModel() {suspend fun login(account: String, password: String): BaseResponse<LoginRsp> {return launchIO { ApiManager.apiService.loginAsync(account, password).await() }}
}
在Presenter 层,启动一个ui Coroutine协程发起 login 请求,model 层就是简单调用 apiService 发起网络请求,然后根据response 判断是否 success 来调用对于 view 的逻辑。这是相对的 “直观”
, 调用方式。 这里存在重复的代码 loginFail
这里 “一般” 都是显示一个 toast 提示用户相关信息。
代码上看起来臃肿,接下来是把这些步骤封装成Kt 扩展函数
阶段二(扩展)
fun launchUI(block: suspend CoroutineScope.() -> Unit, error: ((Throwable) -> Unit)? = null) {presenterScope.launch {tryCatch({block()}, {error?.invoke(it) ?: showException(it.toString())})}
}
我们先抽取一个 launchUI 方法到 Presenter 基类中,封装 Coroutine 启动
方式,方便管理 coroutine。
并添加一个 error Block 默认方法, 如果没有传入 error 参数,默认显示 log (showException
)
fun <R> KResponse<R>.execute(success: (R?) -> Unit, error: ((String) -> Unit)? = null) {if (this.isSuccess()) {success(this.getKData())} else {error?.invoke(this.getKMessage()) ?: showError(this.getKMessage())}
}
然后添加一个 Response 扩展, 处理网络请求的逻辑。这里也是添加两个参数,一个 success,一个 error
(可选参数,默认显示 toast)。下面是 替换成功扩展方法后代码。
class LoginPresenter(view: LoginContract.View) : BasePresenter<LoginContract.View>(view),LoginContract.Presenter {val presenterScope: CoroutineScope by lazy {CoroutineScope(Dispatchers.Main + Job())}override fun login(account: String, password: String) {... 省略部分代码// 启动一个 ui CoroutinelaunchUI({view?.showLoading()LoginModel.login(account, password).execute({ loginRsp ->loginRsp?.let { view?.loginSuc(it) }}, {// TODO loginFail})}, {// TODO loginError})// 省略 TODO 后launchUI({view?.showLoading()LoginModel.login(account, password).execute({ loginRsp ->loginRsp?.let { view?.loginSuc(it) }})})... 省略部分代码}
}
以上是抽取成 一个 扩展函数,简化了 Presenter 处理逻辑。TODO
标识的默认是可以省略,默认是 toast 一个 message。这样调用相对“清晰”
一些。但如果 遇到逻辑复杂的话,会存在 嵌套过深的情况。最近学习了 DSL
(domain-specific language 领域特定语言),引入 DSL 方式优化这些过程。Perfect
!!
阶段三 (DSL)
fun <R> quickLaunch(block: Execute<R>.() -> Unit) {Execute<R>().apply(block)
}inner class Execute<R> {private var successBlock: ((R?) -> Unit)? = nullprivate var failBlock: ((String?) -> Unit)? = nullprivate var exceptionBlock: ((Throwable?) -> Unit)? = nullfun request(block: suspend CoroutineScope.() -> KResponse<R>?) {// LoginModel.login(account, password)launchUI({block()?.run {if (isSuccess()) {successBlock?.invoke(getKData())return@run}failBlock?.invoke(getKMessage()) ?: showError(getKMessage())}}, exceptionBlock)}fun onSuccess(block: (R?) -> Unit) {// loginRsp?.let { view?.loginSuc(it) }this.successBlock = block}fun onFail(block: (String?) -> Unit) {// message?.let { view?.loginFail(it) }this.failBlock = block}fun onException(block: (Throwable?) -> Unit) {// throwable?.let { view?.loginError(it.toString()) }this.exceptionBlock = block}}
DSL看起来比较抽象,在 Presenter 基类里,创建一个 内部类 Execute,声明对应的方法(request, onSuccess, onFail, onException
), (在方法下面注释就是外面 Presenter 传入的 block,便于理解)。先存储对应 block,然后在 request 方法统一处理,具体逻辑和扩展差不多,这里就不多赘述了。我们看看封装成 DSL 后效果。
效果
quickLaunch<LoginRsp> {request { LoginModel.login(account, password) }onSuccess { loginRsp ->loginRsp?.let { view?.loginSuc(it) }}onFail { message ->message?.let { view?.loginFail(it) }}onException { throwable ->throwable?.let { view?.loginError(it.toString()) }}
}
最终效果是不是很“优雅”
,减少了层级嵌套
,从上而下,直观明了,反正我爱了哈哈哈哈。
最后
现已加入肯德基(KtArmor-MVP)豪华午餐,欢迎各位客官品尝~
还是那句话,KtArmor-MVP 封装了基本 MVP结构的框架,是一款小而美的框架,麻雀虽小五章俱全。封装了基础的功能,小的项目,或者测试项目可以直接拿来用,省时省力。希望大家喜欢~
最后,若有不妥,望小伙伴们指出。
Login例子源码
BasePresenter源码
Kotlin的魔能机甲——KtArmor(一)
Kotlin的魔能机甲——KtArmor插件篇(二)
Kotlin的魔能机甲——KtArmor(三)
KtArmor-MVP 源码传送门
Kotlin的魔能机甲——KtArmor网络调用封装(四)相关推荐
- Kotlin的魔能机甲——KtArmor(一)
前言 学习了Kotlin有一段时间了, 每次写项目/Demo的时候, 总是用到网络请求.MVP.MVVM.常用工具类.通用自定义View, 索性把这些整合到一起, 搭成一个Android的脚手架--K ...
- Kotlin的魔能机甲——KtArmor(三)
前言 继上篇说到, KtArmor-MVP的插件使用.我们可以快速创建基本的模板代码,但是在编写业务代码时候,不熟悉KtArmor-MVP框架, 不知其然,无法驾驭这个魔能机甲 .所以这篇我先从Bas ...
- MVVM+Retrofit+Kotlin网络框架封装
上篇文章讲了MVVM入门,网络请求部分非常简单和原始,本篇则是上一篇的进阶,主要讲解如何在vm中使用协程结合Retrofit进行网络框架的封装. GitHub完整版:https://github.co ...
- 【OkHttp】OkHttp 源码分析 ( 网络框架封装 | OkHttp 4 迁移 | OkHttp 建造者模式 )
OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...
- 网络协议(十四):WebSocket、WebService、RESTful、IPv6、网络爬虫、HTTP缓存
网络协议系列文章 网络协议(一):基本概念.计算机之间的连接方式 网络协议(二):MAC地址.IP地址.子网掩码.子网和超网 网络协议(三):路由器原理及数据包传输过程 网络协议(四):网络分类.IS ...
- linux网络编程(四)线程池
linux网络编程(四)线程池 为什么会有线程池? 实现简单的线程池 为什么会有线程池? 大多数的服务器可能都有这样一种情况,就是会在单位时间内接收到大量客户端请求,我们可以采取接受到客户端请求创建一 ...
- Java网络编程(四)—— ServerSocket(一)
Java网络编程(四)-- ServerSocket(一) Java网络编程(四)-- ServerSocket(一) 总述 创建ServerSocket 绑定端口 使用ServerSocket 总述 ...
- 网络取证第四、九章实验
网络取证 第四章案例 一.Ann的异常即时通信[TCP.UDP] 1.课本案例 利用封装协议中的信息 0X4500 IPV4协议数据包的开始 利用TCP/UDP端口号,与默认的标准服务相对应 分析源或 ...
- matlab调用com,com方式调用matlab(四)
目录 com方式调用matlab(一) com方式调用matlab(二) com方式调用matlab(三) com方式调用matlab(四) com方式调用matlab(五) com方式 ...
最新文章
- 黄学东出任微软全球人工智能首席技术官!微软首位华人技术院士全面负责Azure云AI...
- pandas中关于DataFrame计算时间差(加减)
- HLS实现点播和直播时,M3U8文件的不同
- Java char转换为String,String转换为char数组
- 如何将.txt中的数据导入.excel
- 【数据库原理及应用教程】【数据库系统的体系结构】【1.4-1.6】
- icd植入是大手术吗_手术达人|杨兵:ICD植入术一例
- STM8S103之IO复用
- 电脑怎么录制玩王者荣耀的过程
- 4.4 数据的寻址方式(立即寻址、直接寻址、间接寻址、寄存器寻址、相对地址)
- 0517零散问题整理
- FireFox浏览器渗透测试插件
- IE下载时中文文件名乱码解决
- Python的前奏:excel常用功能简介,数据透视表,切片器
- asp.net976-校园论坛系统的设计与实现#毕业设计
- 手把手实现一个element ui 的message
- 时间戳和日期转换工具
- 用js写的轮播图,八位女明星,你翻谁的牌,程序员就是可以为所欲为!
- 正则表达式 匹配两个以上空格
- 氯化聚乙烯(CPE)行业调研报告 - 市场现状分析与发展前景预测