Dart 应用通常只会在单线程中处理它们的工作。并且在大多数情况中,这种模式不但简化了代码而且速度也够快,基本不会出现像动画卡顿以及性能不足这种「不靠谱」的问题。
但是,当你需要进行一个非常复杂的计算时,例如解析一个巨大的 JSON 文档。如果这项工作耗时超过了 16 毫秒,那么你的用户就会感受到不靠谱。
为了避免这种不靠谱的情况,像上面那样消耗性能的计算就应该放在后台处理。在 Android 平台上,这意味着在一个不同的线程中调度工作。而在 Flutter 中,你可以使用一个单独的 Isolate
。
使用步骤
添加 http
包
使用 http
包发起一个网络请求
将响应转换成一列照片
将这个工作移交给一个单独的 isolate
1. 添加 http 包
首先,在你的项目中添加 http
包,http
包会让网络请求变的像从 JSON 端点获取数据一样简单。
2. 发起一个网络请求
在这个例子中,你将会使用 http.get()
方法通过 JSONPlaceholder REST API
获取到一个包含 5000 张图片对象的超大 JSON 文档。
备忘
在这个例子中你需要给方法添加了一个 http.Client
参数。这将使得该方法测试起来更容易同时也可以在不同环境中使用。
3. 解析并将 json 转换成一列图片
接下来,根据 的说明,为了让接下来的数据处理更简单,你需要将 http.Response
转换成一列 Dart 对象。
3.1 创建一个 Photo 类
首先,创建一个包含图片数据的 Photo
类。还需要一个 fromJson
的工厂方法,使得通过 json 创建 Photo
变的更加方便。
class Photo {
final int id;
final String title;
final String thumbnailUrl;
Photo({this.id, this.title, this.thumbnailUrl});
factory Photo.fromJson(Map json) {
return Photo(
id: json['id'] as int,
title: json['title'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
}
}
现在,为了让 fetchPhotos()
方法可以返回一个 Future
,我们需要以下两点更新:>
创建一个可以将响应体转换成 List
的方法:parsePhotos()
在 fetchPhotos()
方法中使用 parsePhotos()
方法
// A function that converts a response body into a List.
List parsePhotos(String responseBody) {
final parsed = json.decode(responseBody).cast
4. 将这部分工作移交到单独的 isolate 中
如果你在一台很慢的手机上运行 fetchPhotos()
函数,你或许会注意到应用会有点卡顿,因为它需要解析并转换 json。显然这并不好,所以你要避免它。
那么我们究竟可以做什么呢?那就是通过 Flutter 提供的 compute()
方法将解析和转换的工作移交到一个后台 isolate 中。这个 compute()
函数可以在后台 isolate 中运行复杂的函数并返回结果。在这里,我们就需要将 parsePhotos()
方法放入后台。
Future> fetchPhotos(http.Client client) async {
final response =
await client.get('https://jsonplaceholder.typicode.com/photos');
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parsePhotos, response.body);
}
4.1 使用 Isolates 需要注意的地方
Isolates 通过来回传递消息来交流。这些消息可以是任何值,它们可以是 null
、num
、bool
、double
或者 String
,哪怕是像这个例子中的 List
这样简单对象都没问题。
当你试图传递更复杂的对象时,你可能会遇到错误,例如在 isolates 之间的 Future
或者 http.Response
。
完整样例
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future> fetchPhotos(http.Client client) async {
final response =
await client.get('https://jsonplaceholder.typicode.com/photos');
// Use the compute function to run parsePhotos in a separate isolate.
return compute(parsePhotos, response.body);
}
// A function that converts a response body into a List.
List parsePhotos(String responseBody) {
final parsed = json.decode(responseBody).cast