布景

事务傍边写Android异步使命一直是一项应战,以往的回谐和线程办理办法比较复杂和繁琐,形成代码难以保护和阅览。在前端范畴中JavaScript其实也面对同样的问题,Promise 就是它的比较干流的一种解法。 在尝试运用Promise之前咱们也针对Android现有的一些异步做了具体的对比。

文章思想导图

运用 promise 重构 Android 异步代码

What:什么是Promise?

关于Android开发的同学,或许许多人不太了解Promise,它主要是前端的实践,所以先解析概念。 Promise 是 JavaScript 言语供给的一种标准化的异步办理办法,它的全体思想是,需求进行 io、等候或者其它异步操作的函数,不回来真实成果,而回来一个“许诺”,函数的调用方能够在合适的机遇,选择等候这个许诺完成(经过 Promise 的 then 办法的回调)。

最简略比方(JavaScript)

const promise = new Promise(function(resolve, reject) {
  // ... some code
  if (/* 异步操作成功 */){
    resolve(value);
  } else {
    reject(error);
  }
}).then(function(value) {
    console.log('resolved.');
}).catch(function(error) {
    console.log('产生过错!', error);
});

实例化一个Promise目标,构造函数承受一个函数作为参数,该参数分别是resolvereject

resolve函数:将Promise 目标状况从pending 变成 resolved

reject函数:将Promise 目标状况从 pending 变成 rejected

then函数:回调 resolved状况的成果 catch函数:回调 rejected状况的成果

能够看到Promise的状况是十分简略且明晰的,这也让它在完成异步编程削减许多认知担负。

Why:为什么要考虑引入Promise

前面说的Promise 不就是 JavaScript 异步编程的一种思想吗,那这跟 Android 开发有什么关系? 尽管前端终端范畴有所不同,但面对的问题其实是大同小异的,比方常见的异步回调导致回调阴间,逻辑处理不连贯等问题。 从事Android开发的同学应该对以下异步编程场景比较了解:

  • 单个网络恳求
  • 多个网络恳求竞速
  • 等候多个异步使命回来成果
  • 异步使命回调
  • 超时处理
  • 守时轮询

这儿能够中止考虑一下,假如运用 Android常规的办法去完成以上场景,你会怎么做?你的脑子或许有以下处理方案:

  • 运用 Thread 创建
  • 运用 Thread + Looper + Handler
  • 运用 Android 原生 AsyncTask
  • 运用 HandlerThread
  • 运用 IntentService
  • 运用 线程池
  • 运用 RxJava 结构

以上方案都能在Android中完成异步使命处理,但或多或少存在一些问题和适用场景,咱们具体剖析下各自的优缺点:

运用 promise 重构 Android 异步代码

经过不同的异步完成办法的对比,能够发现每种完成办法都有适用场景,咱们面对事务复杂度也是不一样的,每一种处理方案都是为了降低事务复杂度,用更低本钱的办法来编码,但咱们也知道代码写出来是给人看的,是需求持续迭代和保护,类似RxJava 这种结构于咱们而言太复杂了,繁琐的操作符容易写出不易保护的代码,简略易理解应该是更好的寻求,而不是炫技,所以咱们才会探索用更轻量更简洁的编码办法来提高团队的代码一致性,就目前而言运用 Promise 来写代码将会有以下优点:

  • 处理回调阴间:Promise 能够把一层层嵌套的 callback 变成 .then().then()... ,从而使代码编写和阅览更直观
  • 易于处理过错:Promise 比 callback 在过错处理上更明晰直观
  • 十分容易编写多个异步操作的代码

How:怎么运用 Promise 重构事务代码?

这儿因为咱们的Java版别的Promise组件未开源,所以本部分只分析重构Case运用案例。

重构case1: 怎么完成一个带超时的网络接口恳求?

这是一段未重构前的获取付款码的异步代码:

运用 promise 重构 Android 异步代码

运用 promise 重构 Android 异步代码
能够看到以上代码存在以下问题:

  • 需求界说异步回调接口
  • 许多 if-else 判别,圈复杂度较高
  • 事务完成了一个超时类,为了不受网络库默许超时影响
  • 逻辑不够连贯,不易于保护

运用 Promise重构后:

运用 promise 重构 Android 异步代码
运用 promise 重构 Android 异步代码

能够看到有以下变化:

  • 消除了异步回调接口,链式调用让逻辑更连贯更明晰了
  • 经过 Promise 包装了网络恳求调用,统一回来 Promise
  • 指定了 Promise 超时时刻,无需额外完成繁琐的超时逻辑
  • 经过 validate 办法 替代 if – else 的判别,假如需求还能够界说校验规矩
  • 统一处理异常过错,逻辑变得愈加完备

重构case2:怎么更高雅的完成长链接降级短链接?

重构前的做法:

运用 promise 重构 Android 异步代码
运用 promise 重构 Android 异步代码
代码存在以下问题:

  • 处理长链接恳求超时,经过回调再处理降级逻辑
  • 运用Handler完成守时器轮询恳求异步成果并处理回调
  • 处理各种逻辑判别,代码难以保护
  • 不易于模仿超时降级,代码可测验性差

运用Promise重构后:

运用 promise 重构 Android 异步代码
运用 promise 重构 Android 异步代码

第一个Promise处理长链接Push监听 ,设置5s超时,超时异常产生回调except办法,判别throwable 类型,假如为PromiseTimeoutException实例目标,则履行降级短链接。短链接是另外一个Promise,经过这种办法将逻辑都彻底成果,代码不会分裂,逻辑更连贯。 短链接轮训查单逻辑运用Promise完成:

运用 promise 重构 Android 异步代码

  • 最外层Promise,操控全体的超时,即不管轮询的成果怎么,超过限守时刻直接给定失利成果
  • Promise.delay(),这个比较细节,咱们认定500ms轮询必定不会回来成果,则经过推迟的办法来削减一次轮询恳求
  • Promise.retry(),真正重试的逻辑,限制了最多重试次数和延时逻辑,RetryStrategy界说的是重试的战略,推迟(delay)多少和满意怎样的条件(condition)才答应重试

这段代码把复杂的延时、条件判别、重试战略都经过Promise这个结构完成了,少了许多临时变量,代码量更少,逻辑更明晰。

重构case3:完成 iLink Push支付音讯和短链接轮训查单竞速

后面针对降级战略重构成竞速模型,选用Promise.any很轻松得完成代码重构,代码如下图所示。

运用 promise 重构 Android 异步代码

总结

本文供给一种异步编程的思路,借鉴了Promise思想来重构了Android的异步代码。经过Promise组件供给的多种并发模型能够更高雅的处理绝大部分的场景需求。

防踩坑指南

假如跟Activity或Fragment生命周期绑定,需求在生命周期结束时,撤销掉promise的线程运转,否则或许会有内存泄露;这儿能够选用AbortController来完成更高雅的中断 Promise。

并发模型

● 多使命并行恳求

Promise.all():承受任意个Promise目标,并发履行异步使命。悉数使命成功,有一个失利则视为全体失利。

Promise.allSettled(): 使命优先,一切使命有必要履行结束,永久不会进入失利状况。

Promise.any():承受任意个Promise目标,并发履行异步使命。等候其中一个成功即为成功,悉数使命失利则进入过错状况,输出过错列表。

● 多使命竞速场景

Promise.race(): 承受任意个Promise目标,并发履行异步使命。时刻是第一优先级,多个使命以最先回来的那个成果为准,此成果成功即为全体成功,失利则为全体失利。

扩展考虑

  1. Promise 最佳实践
  1. 防止过长的链式调用:尽管Promise能够经过链式调用来防止回调阴间,可是假如Promise的链过长,代码的可读性和保护性也会变差。
  2. 及时针对Promise进行abort操作:Promise运用不当或许会形成内存泄露,比方未调用abort,页面撤销未及时销毁proimse。
  3. 需求处理except异常回调,处理PromiseException.
  4. 能够运用validation来完成规矩校验,削减if-else的规矩判别
  1. Java Promise 组件完成原理
  1. 状况机完成(pending、fulfilled、rejected)
  2. 默许运用 ForkJoinPool 线程池,适合核算密集型使命。针对阻塞IO类型,能够运用内置ThreadPerTaskExecutor 简略线程池模型。
  1. Promise vs Kotlin协程

Promise 链式调用,代码明晰,上手本钱较低;底层完成仍然是线程,经过线程池办理线程调度 Koitlin 协程,更轻量的线程,运用比较灵敏,能够由开发者操控,比方挂起和恢复

  1. 可测验性的考虑

依据 Promise 的特色,能够经过Mock状况(resolve、reject、outTime)来完成模仿成功,回绝、超时; 完成思路: ● 自界说注解类辅佐定位Hook点 ● 运用ASM字节码对Promise 进行代码插桩

附录

Promise – JavaScript | MDN

Promises/A+

欢迎重视我的大众号,一同前进~

运用 promise 重构 Android 异步代码