5

Flutter 下载篇 - 叁 | 网络库切换实践与思考 - 睡觉谁叫

 1 year ago
source link: https://www.cnblogs.com/xuge2it/p/17178595.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

本文是关于使用flutter_download_manager下载功能的实践和探索。我们将基于flutter_download_manager的功能扩展,改造成自己想要的样子。在阅读本文之前,建议先了解前两篇文章:

本文将基于第二篇中的扩展框架,将网络库从dio切换为httpclient,并结合改造过程中发现的问题提出自己的想法。

优化点:dynamic的告警问题

Untitled.png

在第2和20行中,黄色标记表明,如果第2行中的每个网络库下载的返回值可能不同,则考虑将其设置为“dynamic”,这可能导致第20行中出现响应状态码的告警,因为该属性可能不存在。

为了确保 download 接口的返回值具有 statusCode 属性,在这里定义了一个专门的返回类以进行限制。具体定义如下:

Untitled 1.png

这样就解决了statusCode告警问题,其中extra可以存放原始download response对象。

HttpClient实现网络库

只用实现上一篇中的网络接口CustomHttpClient,然后修改IDownloader:createObject其中网络注入对象即可,如下:

//实现这个接口定义
abstract class CustomHttpClient {
  Future<DownloadHttpResponse> download(
    String urlPath,
    String savePath, {
    DownloadProgressCallback? onReceiveProgress,
    DownloadCancelToken? cancelToken,
    Map<String, dynamic>? options,
  });

  DownloadCancelToken generateToken();
}

------【idownloader.dart】----------
abstract class IDownloader {
  factory IDownloader() => createObject(
        //将这个注入修改成我们实现的即可 原来:customHttpClient: CustomDioImpl(),
        customHttpClient: CustomHttpClientImp(),
      );
}

实现代码:

Untitled 2.png
  • 第9-17行:主要是将flutter_download_manager中已下载但未下载完整的文件大小传递给后端,以便告知后端从哪里继续下载文件。

如果不传,会浪费带宽和时间。在处理大文件时,内存压力会增大,中断的可能性也会增加。此外,用户界面可能会出现进度条跳跃的问题。

  • 第27-45行:将下载流写入传入的 savepath 文件中。需要注意 cancelToken.isCancelled 方法,因为上一篇中没有定义 isCancelled 属性,这里必须在 DownloadCancelToken 中提供该方法(第69行)。
  • 第55-65行:这里实现了HttpClientCancelToken的cancel方法,具体实现就是给标志位_isCancelled赋值。

遇到官方问题

完成上述实践后,发现官方进度错误BUG。如果多次暂停、取消,然后再恢复下载,会出现进度起始位置错误的问题。下载会从已下载文件的长度开始,效果如下所示:

221539959-e5af41bc-b3b1-41cc-9a46-1ba549c4fd86.gif

在暂停时,暂停前未将下载流写入已下载的文件中。

如果用户点击了暂停,会抛出取消异常,此时捕获该异常并判断当前下载任务状态是暂停态,将已下载的数据流写入未下载完全的文件中。

已提交PR到官方库中,见PR地址

完整代码传送门,其中包含了httpclient实现和上述官方进度问题修复方案。

回顾网络库解耦

在切换flutter_download_manager网络库时,我们发现解耦方案仍然存在问题。

1. isCanceled

在httpclient中使用了isCancelled方法,不得不将其加入DownloadCancelToken中,这在设计上是有问题的。

我查看了dio的download过程,发现其中也存在对取消状态的判断。dio.CancelToken类中也定义了这个方法,那么为什么我没有考虑到呢?原因是我没有实践过,当时只是用downloadTokenProxy去代理了CancelToken,它可以跑,就认为设计没有问题。果然,自己挖的坑需要自己踩一遍才能真正理解其中的问题。

2. flutter_download_manager框架运行约束

为了让该库正常运行,必须与相关的网络库配合使用。

在我使用httpclient进行实现过程中,我发现如果取消操作,必须抛出一个异常(请参考代码中第32行),才能确保程序能够顺利地执行case1而不出现官方文档中提到的问题。

Future<void> download(
      String url, String savePath, DownloadCancelToken cancelToken,
      {forceDownload = false}) async {
    late String partialFilePath;
    late File partialFile;
    try {
      var task = getDownload(url);
        var response = await customHttpClient.download(...);
      } else {
        var response = await customHttpClient.download(...
        );
      }
    } catch (e) {
      var task = getDownload(url)!;
      if (task.status.value != DownloadStatus.canceled &&
        //...
      } else if (task.status.value == DownloadStatus.paused) {
        // 只有抛出取消异常才能走到保持下载流到未下载完全文件中 case1
        final ioSink = partialFile.openWrite(mode: FileMode.writeOnlyAppend);
        final f = File(partialFilePath + tempExtension);
        final fileExist = await f.exists();
        if (fileExist) {
          await ioSink.addStream(f.openRead());
          await f.delete();
        }
        await ioSink.close();
      }
    }

约束一:如果需要取消任务,应该抛出取消异常。

因为flutter_download_manager一开始网络库就是绑定的dio,而dio中对取消操作的结果反馈就是取消异常。如果用户取消了任何一个请求,就会抛出该异常。

话说,取消发送一条消息难道非得抛出异常才可以吗?其实有很多方法可以实现这个功能。

约束二:请提供下载请求的返回码。

由于flutter_download_manager已经处理了返回码206和200,如果不提供网络请求返回码,相关逻辑无法执行。

话说,请求成功返回结果的方式也可以是发消息吧。

下载框架设计思路

如果将flutter_download_manager作为代码片段使用是没有问题的,但从下载框架设计的角度来看,仍需要进一步改进和优化。

出现上述提到的约束问题,主要是将关系集中在DownloadManager和网络库上,陷入网络细节中。实际上,这两者没有直接关系,主要是flutter_download_manager作者将它们耦合在一起导致的。

从下载框架角度说,类之间依赖关系应该如下:

Untitled 3.png

DownloadManager依赖下载器,下载器依赖网络库

三者间交互关系如下:

Untitled 4.png
  • DownloadManager 通过维护列表来管理内部任务的增删改查。每个任务对应一个下载过程。
  • Downloader 负责任务下载,并通过同步或异步消息通知当前下载任务的状态。DownloadManger 通过这些消息来更新任务列表。
  • Downloader 通过向网络库发送请求来下载任务。网络将结果返回给 Downloader,由 Downloader 来决定内部状态和断点续传逻辑。

本文介绍了Flutter下载功能的实践和探索,包括网络库的切换和优化。使用了httpclient实现网络库,并解决了官方进度错误BUG。还回顾了flutter_download_manager的设计缺陷,并提出了下载框架的设计思路。总之,提供了有关Flutter下载功能的实用信息和思考。

太棒了!鼓励自己坚持到底。我希望我为你投入的时间增加了一些价值。

如果觉得文章对你有帮助,点赞、收藏、关注、评论,一键四连支持,你的支持就是我创作最大的动力。

❤️本文由公众号编程黑板报 原创,关注我,获取我的最新文章~❤️


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK