likes
comments
collection
share

【Flutter】继承、扩展、混入的实战,如何实现 Dio 的请求生命周期自动化

作者站长头像
站长
· 阅读数 12

前言

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

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

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

实现的方式有很多,这里列举三种方式:一种是我们常用的基类继承的封装,另一种是使用扩展方法实现,还有一种是混入实现。

关于继承与实现,扩展,混入,我都在之前都出过单独的文章,本文也算是对它们进行一种总结与实战,可以通过本文更加的理解这几种效果的相同点与不同点。

下面就分别看看如何实现吧。

一、基类的实现

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

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();
  }
  
  // 其他公共方法或属性 ...
}

使用的时候:

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坑,那真是找都不好找。

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 了:

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 加了一些奇怪的逻辑,他就能点进去看看你的混入逻辑,比较方便调试。

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();
  }
}

使用的时候:

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

【Flutter】继承、扩展、混入的实战,如何实现 Dio 的请求生命周期自动化