前言

继上次分享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网络调用封装(四)相关推荐

  1. Kotlin的魔能机甲——KtArmor(一)

    前言 学习了Kotlin有一段时间了, 每次写项目/Demo的时候, 总是用到网络请求.MVP.MVVM.常用工具类.通用自定义View, 索性把这些整合到一起, 搭成一个Android的脚手架--K ...

  2. Kotlin的魔能机甲——KtArmor(三)

    前言 继上篇说到, KtArmor-MVP的插件使用.我们可以快速创建基本的模板代码,但是在编写业务代码时候,不熟悉KtArmor-MVP框架, 不知其然,无法驾驭这个魔能机甲 .所以这篇我先从Bas ...

  3. MVVM+Retrofit+Kotlin网络框架封装

    上篇文章讲了MVVM入门,网络请求部分非常简单和原始,本篇则是上一篇的进阶,主要讲解如何在vm中使用协程结合Retrofit进行网络框架的封装. GitHub完整版:https://github.co ...

  4. 【OkHttp】OkHttp 源码分析 ( 网络框架封装 | OkHttp 4 迁移 | OkHttp 建造者模式 )

    OkHttp 系列文章目录 [OkHttp]OkHttp 简介 ( OkHttp 框架特性 | Http 版本简介 ) [OkHttp]Android 项目导入 OkHttp ( 配置依赖 | 配置 ...

  5. 网络协议(十四):WebSocket、WebService、RESTful、IPv6、网络爬虫、HTTP缓存

    网络协议系列文章 网络协议(一):基本概念.计算机之间的连接方式 网络协议(二):MAC地址.IP地址.子网掩码.子网和超网 网络协议(三):路由器原理及数据包传输过程 网络协议(四):网络分类.IS ...

  6. linux网络编程(四)线程池

    linux网络编程(四)线程池 为什么会有线程池? 实现简单的线程池 为什么会有线程池? 大多数的服务器可能都有这样一种情况,就是会在单位时间内接收到大量客户端请求,我们可以采取接受到客户端请求创建一 ...

  7. Java网络编程(四)—— ServerSocket(一)

    Java网络编程(四)-- ServerSocket(一) Java网络编程(四)-- ServerSocket(一) 总述 创建ServerSocket 绑定端口 使用ServerSocket 总述 ...

  8. 网络取证第四、九章实验

    网络取证 第四章案例 一.Ann的异常即时通信[TCP.UDP] 1.课本案例 利用封装协议中的信息 0X4500 IPV4协议数据包的开始 利用TCP/UDP端口号,与默认的标准服务相对应 分析源或 ...

  9. matlab调用com,com方式调用matlab(四)

    目录 com方式调用matlab(一) com方式调用matlab(二) com方式调用matlab(三) com方式调用matlab(四) com方式调用matlab(五)        com方式 ...

最新文章

  1. 黄学东出任微软全球人工智能首席技术官!微软首位华人技术院士全面负责Azure云AI...
  2. pandas中关于DataFrame计算时间差(加减)
  3. HLS实现点播和直播时,M3U8文件的不同
  4. Java char转换为String,String转换为char数组
  5. 如何将.txt中的数据导入.excel
  6. 【数据库原理及应用教程】【数据库系统的体系结构】【1.4-1.6】
  7. icd植入是大手术吗_手术达人|杨兵:ICD植入术一例
  8. STM8S103之IO复用
  9. 电脑怎么录制玩王者荣耀的过程
  10. 4.4 数据的寻址方式(立即寻址、直接寻址、间接寻址、寄存器寻址、相对地址)
  11. 0517零散问题整理
  12. FireFox浏览器渗透测试插件
  13. IE下载时中文文件名乱码解决
  14. Python的前奏:excel常用功能简介,数据透视表,切片器
  15. asp.net976-校园论坛系统的设计与实现#毕业设计
  16. 手把手实现一个element ui 的message
  17. 时间戳和日期转换工具
  18. 用js写的轮播图,八位女明星,你翻谁的牌,程序员就是可以为所欲为!
  19. 正则表达式 匹配两个以上空格
  20. 氯化聚乙烯(CPE)行业调研报告 - 市场现状分析与发展前景预测

热门文章

  1. 1166:The Clocks
  2. Unsafe.compareAndSwapInt()方法解读
  3. 身份证上的号码代表的意思大揭秘
  4. 载荷是什么意思?底层原理是什么?
  5. 工业微型计算机视频,工业微型计算机辅导
  6. laravel Whoops, looks like something went wrong.
  7. 运营商的“100Mbps”非网速100Mb/s
  8. ESXi开启SSH登录权限
  9. iOS打电话、发短信、发邮件功能
  10. 十五、Jdbc操作数据库