【Flutter】继承、扩展、混入的实战,如何实现 Dio 的请求生命周期自动化
前言
在之前客户端的开发中,例如 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,这一期就此完结。
转载自:https://juejin.cn/post/7329438490467041289