如何优化此 HTTP 请求方法以在 UI 线程中处理冻结?

How Optimize this HTTP Request Method for Dispose Freezes in UI Thread?

当我 运行 这个方法时,在 flutter 中,例如当我 onTap 在手势检测器中 flutter 冻结 2 次,当 get 方法调用时(我认为那里是因为Dio json 转换器,也许,idk),当 base64Decode 方法调用时

在 flutter web 上,当 get 方法调用时,flutter 完全崩溃(onSendProgress 此时也无法正常工作)并且在 chrome 中的此应用程序崩溃后代码 5 ,一两分钟后 -_-

context 不是 BuildContext

httpClient 是 Dio HTTP 客户端

transformOf检查请求结果是否错误,如果是,则将请求结果转换为我的flutter自定义异常,抛出后,否则return请求结果。

请求结果大小为20-50mb

Future<Uint8List> readBookFile(Identificator groupId, Identificator bookId) async {
  final result = await context.httpClient.get(
    '/book/file',
    queryParameters: {
      'groupId': groupId.toString(),
      'bookId': bookId.toString(),
    },
  ).transformOf(context);

  return base64Decode(result['bookBytes']);
}

如何优化此代码以处理冻结?

默认情况下,Flutter 应用程序在单个 UI 线程上完成所有工作。如果您尝试在该 UI 线程内执行昂贵的计算,您的应用程序的 UI 会冻结。

问题

在你的情况下 base64Decode(result['bookBytes']); 是一个非常昂贵的计算。 (因为它解码大数据集(20-50mb))

解决方案

您可以通过为 运行 上述昂贵的任务使用 单独的 Isolate 来解决这个问题。 (见下面的代码)

  1. 创建一个名为 parseBookBytes() 的方法,将昂贵的 baseDecode64() 任务放入其中。

  2. 使用 compute() 函数在单独的隔离中执行 parseBookBytes()

// A function that converts a result into a Uint8List.
Uint8List parseBookBytes(var bookBytes) {
  return base64Decode(bookBytes);
}

Future<Uint8List> readBookFile(Identificator groupId, Identificator bookId) async {
  final result = await context.httpClient.get(
    '/book/file',
    queryParameters: {
      'groupId': groupId.toString(),
      'bookId': bookId.toString(),
    },
  ).transformOf(context);

  return compute(parseBookBytes, result['bookBytes']);   //run on a separate isolate using compute() function
}

资源

  1. Link 1
  2. Link 2
  3. Link 3

为了解决这个问题,我创建了 2 个解析函数和 2 个计算函数

Uint8List parseBase64(String base64) {
  return base64Decode(base64);
}

Future<Uint8List> parseBase64Compute(String base64) {
  return compute<String, Uint8List>(parseBase64, base64.trim());
}

Map<String, dynamic> parseJson(String json) {
  return jsonDecode(json);
}

Future<Map<String, dynamic>> parseJsonCompute(String base64) {
  return compute<String, Map<String, dynamic>>(parseJson, base64);
}

在我为 json

创建简单的 dio 转换器之后
class JsonTransformer extends DefaultTransformer {
  JsonTransformer() : super(jsonDecodeCallback: parseJsonCompute);
}

并应用这个

httpClient.transformer = JsonTransformer();

重构函数为

Future<Uint8List> readBookFile(Identificator groupId, Identificator bookId) async {
  final result = await context.httpClient.get(
    '/book/file',
    queryParameters: {
      'groupId': groupId.toString(),
      'bookId': bookId.toString(),
    },
  ).transformOf(context);

  return parseBase64Compute(result['bookBytes']);;
}