【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,这一期就此完结。

相关推荐
程序员老刘5 小时前
Dart的宏取消了,期待3年的功能,说没就没了?
flutter·客户端·dart
Asthenia04126 小时前
技术复盘:从一次UAT环境CORS故障看配置冗余的危害与最佳实践
前端
csj506 小时前
前端基础之《React(1)—webpack简介》
前端·react
被巨款砸中6 小时前
前端 20 个零依赖浏览器原生 API 实战清单
前端·javascript·vue.js·web
文韬_武略6 小时前
web vue之状态管理Pinia
前端·javascript·vue.js
mosen8686 小时前
【Vue】Vue Router4x关于router-view,transtion,keepalive嵌套写法报错
前端·javascript·vue.js
写不来代码的草莓熊7 小时前
vue前端面试题——记录一次面试当中遇到的题(5)
前端
weixin_ab7 小时前
【HTML分离术】
前端·html
文心快码BaiduComate8 小时前
新手该如何选择AI编程工具?文心快码Comate全方位体验
前端·后端·程序员
夫唯不争,故无尤也8 小时前
Tomcat 内嵌启动时找不到 Web 应用的路径
java·前端·tomcat