【Flutter】基于 GetX 实现 Dio 的生命周期自动化

前言

在前文【传送门】的网络请求封装中我们基于DIO的网络请求进行了封装,本身是没什么问题的,但是细想之下,我们并没有处理取消网络的逻辑。

在之前客户端的开发中,例如 Android 的 ViewModel 中我们不管是通过协程的绑定生命周期自动取消,还是使用 RxJava/RxKotlin 的方式手动收集 Disposable 并且在销毁的时候释放,我们都是需要做处理的。

而在 Flutter 中由于我们是基于 Get 框架实现的,那么在 GetxController 中我们并没有处理网络请求的取消,这样就可能会发生内存泄露、占用带宽和系统资源、空指针等。

所以我们需要考虑如何在 GetxController 中实现自动化的 DIO 的 CancelTag 管理。

实现的方式有很多,这里列举三种方式:一种是我们常用的基类封装,另一种是使用扩展方法实现,还有一种是混入实现,下面就分别看看如何实现吧。

一、基类的实现

其实我个人不推荐这种方法,不太优雅,因为可能我们自己封装了一个 BaseController 除了你自己其他人接触到你的项目就是一脸懵,直接改变了原本类,容易让人摸不着头脑。

scala 复制代码
import 'package:dio/dio.dart';
import 'package:get/get.dart';

class BaseController extends GetxController {
  // Dio 实例可以从外部传入,也可以在BaseController中创建
  final Dio dio;
  // 创建一个 CancelToken 用于取消Dio请求
  final CancelToken cancelToken = CancelToken();
  
  BaseController({required this.dio});

  @override
  void onClose() {
    // 当控制器关闭时,取消所有的 Dio 请求
    cancelToken.cancel("Controller has been disposed");
    super.onClose();
  }
  
  // 其他公共方法或属性 ...
}

使用的时候:

scala 复制代码
class MyController extends BaseController {
  MyController({required Dio dio}) : super(dio: dio);

  void fetchData() async {
    try {
      final response = await dio.get(
        'https://example.com/data',
        cancelToken: cancelToken,
      );
      // 处理响应 ...
    } catch (e) {
      if (CancelToken.isCancel(e)) {
        print("请求取消:$e");
      } else {
        // 处理其他错误 ...
      }
    }
  }
  
  // 其他方法 ...
}

此时就是所有的 Controller 都需要继承你自定义的 BaseController,以后有什么逻辑就往 Base 里面加,反正能实现我的需求了。

二、扩展实现

使用扩展可能实现类似的功能,并且没有改变本身的 Controller 是继承与 GetxController 这个对象,让同事或者其他开发者一目了然。

但是缺点是扩展的逻辑不明显,如果你不告诉其他开发者,它甚至都不知道你在里面添加了什么逻辑,如果是 CancelToken 这种无感逻辑还好,如果是一些特定的逻辑或者有一些Bug坑,那真是找都不好找。

java 复制代码
import 'package:dio/dio.dart';
import 'package:get/get.dart';

extension CancelableGetController on GetxController {
  // 关联 CancelToken
  CancelToken _cancelToken = CancelToken();

  // 获取CancelToken的方法
  CancelToken get cancelToken => _cancelToken;

  // 取消请求的方法
  void cancelRequests() {
    _cancelToken.cancel("请求被取消");
  }

  // 为了确保自动取消网络请求,在类销毁时调用cancelRequests
  @override
  void onClose() {
    cancelRequests();
    super.onClose();
  }
}

我们使用扩展就无需使用 BaseController 了:

scala 复制代码
class MyController extends GetxController {
  final Dio dio = Dio();

  void fetchSomething() async {
    try {
      final response = await dio.get(
        'https://myapi.com/data',
        cancelToken: cancelToken, // 使用扩展提供的CancelToken
      );
      // 处理响应
    } catch (e) {
      // 错误处理
    }
  }
}

可以说是使用简单,逻辑无感,适用与一些特定场景。

三、混入实现

使用 mixin 的方式呢,和扩展比较类似,只是可以选装了,比如我这个 SettingController 它没有网络请求,只有一些本地数据处理逻辑,那么我就可以选择不混入 DIO 的 CancelToken 逻辑,如果我是 UserProfileController 这肯定是有网络请求我就可以选择混入DIO 的 CancelToken 逻辑。

可选装是他最大的有点,其他开发者可能一目了然,哦,你这个 Controller 加了一些奇怪的逻辑,他就能点进去看看你的混入逻辑,比较方便调试。

dart 复制代码
import 'package:dio/dio.dart';
import 'package:get/get.dart';

mixin CancelableMixin on GetxController {
  CancelToken _cancelToken = CancelToken();

  CancelToken get cancelToken => _cancelToken;

  @override
  void onClose() {
    _cancelToken.cancel("请求被取消");
    super.onClose();
  }
}

使用的时候:

scala 复制代码
class MyController extends GetxController with CancelableMixin {
  final Dio dio = Dio();

  void fetchSomething() async {
    try {
      final response = await dio.get(
        'https://myapi.com/data',
        cancelToken: cancelToken,
      );
      // 处理响应
    } catch (e) {
      // 错误处理
    }
  }
}

总结

三种方式都可以实现类似的效果,如果你就像使用基类完全没问题,如果你想隐藏你的业务逻辑使用扩展的方法很难被人发现,如果你想做成可配置的效果,更加极限的内存优化,那么使用混入的方式比较适合你。

什么?我用的哪一种?我用的混入方式,不过我在魔改 GetxController 其他方式也用到了扩展方法,对于一些必须存在的自定义且无感的逻辑我还是喜欢用扩展的方式,如果是可用可不用这种配置选项我更喜欢用混入的方式。

那么本期内容就到这里,如讲的不到位或错漏的地方,希望同学们可以评论区指出。

本文的代码已经全部贴出,部分没贴出的代码可以在前文中找到,我的 Flutter Demo 项目正在整理中,后期开源了会更新文章链接。

如果感觉本文对你有一点点的启发,还望你能点赞支持一下,你的支持是我最大的动力啦!

Ok,这一期就此完结。

相关推荐
世俗ˊ23 分钟前
CSS入门笔记
前端·css·笔记
子非鱼92123 分钟前
【前端】ES6:Set与Map
前端·javascript·es6
6230_28 分钟前
git使用“保姆级”教程1——简介及配置项设置
前端·git·学习·html·web3·学习方法·改行学it
想退休的搬砖人37 分钟前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js
加勒比海涛1 小时前
HTML 揭秘:HTML 编码快速入门
前端·html
啥子花道1 小时前
Vue3.4 中 v-model 双向数据绑定新玩法详解
前端·javascript·vue.js
麒麟而非淇淋1 小时前
AJAX 入门 day3
前端·javascript·ajax
茶茶只知道学习1 小时前
通过鼠标移动来调整两个盒子的宽度(响应式)
前端·javascript·css
清汤饺子1 小时前
实践指南之网页转PDF
前端·javascript·react.js
蒟蒻的贤1 小时前
Web APIs 第二天
开发语言·前端·javascript