• 注册
  • Android博客 Android博客 关注:0 内容:1372

    Flutter 入门与实战(三十):Dio之戛然而止

  • 查看作者
  • 打赏作者
  • 当前位置: 职业司 > Android开发 > Android博客 > 正文
    • Android博客
    • 甜蜜的约会

      程序员小明今天很开心,因为今天是他和女朋友的恋爱一周年纪念日。众所周知,程序员要找女朋友是很难的,小明目前是他们办公室唯一脱单的程序员,成为了众多程序员艳羡的对象。小明今天的工作效率也很高,键盘敲得都要飞起来了,整个办公室都响着他快乐的键盘敲击声。到了下班时间,小明早已提交好代码,飞奔下楼奔赴约会了——当然,他没有忘记买花。

      女朋友见了小明也是非常开心,这一天的安排俩人也早就计划好了。一对小年轻回忆起这一年的经历,甜言蜜语说个不停——俩人引起了不少背着双肩包、穿着格子衫的人羡慕的眼光。

      Flutter 入门与实战(三十):Dio之戛然而止

      电话响了

      到了晚上9点多,俩人的浪漫晚餐结束的时候,小明的女朋友说:“咱们回去吧!”,小明心领神会,正准备牵女朋友手往外走的时候,电话响了!电话响了!电话响了!小明心里预感不妙,收回刚要伸出的手,从口袋里掏出了手机。看到手机上显示的名字,他要崩溃了!那是他们领导打来的电话。
      “小明,赶紧回公司,你今天提交的代码出Bug 了!”
      “呃,很紧急吗?”
      “紧急啊!不紧急用得着打电话给你吗?”
      “那……那我马上回去。”
      小明无奈地看了一眼女朋友。
      “领导让我回去改 Bug!”
      “不能明天再去吗?我们正在约会唉!”女朋友一脸不高兴。
      “不行哦!我们程序员发现 Bug 要马上改!”
      小明说完,背起他的双肩包赶紧往公司赶去,留下女朋友一个人呆呆地站在那里。小明没有听见女朋友的一句话:“我觉得我们不合适……”

      言归正传

      上述的故事在我们的日常生活很常见,改 Bug 嘛,那不是我们程序员赖以生存的根基么?实际上,一件事情被打断经常发生,在网络请求里也一样。比如说,刚进入一个页面,网络请求还在进行,然后用户又退出这个页面了。那这时候的请求其实没什么意义,如果是简单的请求还好,但如果是下载文件、进行一系列请求的时候,如果能够取消请求就好了。

      Dio 提供了取消令牌(CancelToken)机制用于取消尚未完成的请求。每个请求都可以携带一个 CancelToken 对象,当调用 CancelToken 的 cancel 方法时,就会通知该请求停止当前的请求,从而达到中断请求的目的。这就好比是小明正在约会的时候,被领导的电话叫去改 Bug 一样,他的约会相当于泡汤了!

      CancelToken 的使用

      我们先看一个简单的示例,首先在我们的 HttpUtil 类的各类方法都加上了可选的参数 cancelToken,并且在检测到取消后显示对应的提示。

      static Future sendRequest(HttpMethod method, String url,
      {Map<String, dynamic> queryParams,
      dynamic data,
      CancelToken cancelToken}) async {
      try {
      //...省略请求代码
      } on DioError catch (e) {
      // 检测错误是不是因为取消请求引起的,如果是打印取消提醒
      if (CancelToken.isCancel(e)) {
      EasyLoading.showInfo('领导喊你回去改 Bug 啦!');
      } else {
      EasyLoading.showError(e.message);
      }
      } on Exception catch (e) {
      EasyLoading.showError(e.toString());
      }
      return null;
      }
      复制代码

      然后我们模拟一个取消请求的情况。我们请求的是掘金的个人主页的文章列表,首先创建了一个 CancelToken 对象,然后传给对应的请求。发出请求后,我们马上调用了 cancel方法取消请求。这里不能使用 async 和 await。因为 await 会等待请求完成,因此这里使用的是 Future 的 then 方法(类似 Promise)。接收到响应后,我们根据 CancelToken 对象的 isCancelled 属性来判断请求是否被取消,并且更新状态变量_hasBug。

      void _bugHappened() {
      CancelToken token = CancelToken();
      JuejinService.listArticles('70787819648695', cancelToken: token)
      .then((value) => {
      setState(() {
      _hasBug = token.isCancelled;
      })
      })
      .onError((error, stackTrace) => {print(error.toString())});
      token.cancel();
      }
      复制代码

      _hasBug 用于控制界面显示,表示是否有 Bug,如果没有 Bug 那小明可以继续约会,如果有 Bug 那小明得赶回公司改 Bug。我们通过一个按钮来触发 bug 事件。

      class _AppointmentPageState extends State<AppointmentPage> {
      bool _hasBug = false;
      @override
      Widget build(BuildContext context) {
      return Scaffold(
      appBar: AppBar(
      title: Text(_hasBug ? '该死的Bug!' : '约会中...',
      style: Theme.of(context).textTheme.headline4),
      ),
      body: Container(
      child: Center(
      child: Image.asset(_hasBug ? 'images/bug.png' : 'images/dating.png'),
      ),
      ),
      floatingActionButton: IconButton(
      icon: Icon(Icons.call),
      onPressed: () {
      _bugHappened();
      },
      ),
      );
      }
      //...
      }
      复制代码

      运行结果

      运行结果如下图所示,可以看到点击按钮后出现了请求被取消事件——小明得回去改 Bug 了(小明心中一万头草泥马飘过)!

      Flutter 入门与实战(三十):Dio之戛然而止

      实际应用

      假设我们退出页面前要取消未完成的请求,就可以使用 CancelToken 来取消了。我们可以在 State生命周期函数的 deactivate 方法(该方法在 dispose 前会被调用)调用 CancelToken 的取消方法。为了演示效果,我们来一个请求比较慢的网站——Github。
      由于 CancelToken 对象在不同的方法使用,因此需要定义为成员属性,完整代码如下。

      class _AppointmentPageState extends State<AppointmentPage> {
      bool _hasBug = false;
      CancelToken _token;
      @override
      Widget build(BuildContext context) {
      return Scaffold(
      appBar: AppBar(
      title: Text(_hasBug ? '该死的Bug!' : '约会中...',
      style: Theme.of(context).textTheme.headline4),
      ),
      body: Container(
      child: Center(
      child: Image.asset(_hasBug ? 'images/bug.png' : 'images/dating.png'),
      ),
      ),
      floatingActionButton: IconButton(
      icon: Icon(Icons.call),
      onPressed: () {
      _bugHappened();
      },
      ),
      );
      }
      void _bugHappened() {
      _token = CancelToken();
      HttpUtil.get('https://www.github.com', cancelToken: _token)
      .then((value) => {
      if (mounted)
      {
      setState(() {
      _hasBug = _token.isCancelled;
      })
      }
      })
      .onError((error, stackTrace) => {});
      }
      @override
      void deactivate() {
      if (_token != null) {
      _token.cancel('dispose');
      }
      super.deactivate();
      }
      }
      复制代码

      业务流程如下:

      • 进入界面后,点击底部的电话图标按钮开始请求
      • 点击返回界面时检查_token 是否为空,不为空则调用 cancel 方法取消请求。cancel 方法可以接收一个可选的参数,用于表名取消的原因。

      如果网络状况不太好而你的手速有足够快的话(打王者的技巧派上用场了),就可以看到返回后会显示一个提醒,说明我们的请求被取消了。

      这里需要注意,由于我们在网络请求的 then 回调调用了 setState 方法,这个方法在 dispose后是不能调用的,否则可能导致内存泄露。因此在调用前我们判断了一下 mounted 是否为 true,如果是则表示没有被 dispose,可以安全地调用 setState 方法。

      Flutter 入门与实战(三十):Dio之戛然而止

      我们下一篇来研究一下 Dio 这块的源码,看看 CancelToken 的实现机制。

      后记

      忠告各位程序员,重要的话说三遍:约会请记得关机!约会请记得关机!约会请记得关机!

      请登录之后再进行评论

      登录

      手机阅读天地(APP)

      • 微信公众号
      • 微信小程序
      • 安卓APP
      手机浏览,惊喜多多
      匿名树洞,说我想说!
      问答悬赏,VIP可见!
      密码可见,回复可见!
      即时聊天、群聊互动!
      宠物孵化,赠送礼物!
      动态像框,专属头衔!
      挑战/抽奖,金币送不停!
      赶紧体会下,不会让你失望!
    • 实时动态
    • 签到
    • 做任务
    • 发表内容
    • 偏好设置
    • 到底部
    • 帖子间隔 侧栏位置:
    • 还没有账号?点这里立即注册