【译】Flutter :Parse JSON in the ba
原文为Flutter官方的Cookbook:Parse JSON in the background
通常,Dart app 会在一个线程内完成所有工作。许多情况下,这个模型简化了编码,速度足够快而不会导致应用程序性能差或动画断断续续,通常称为“jank”。
然而,你可能需要执行一些耗时的操作,例如:解析一个非常大的Json
文件。如果这个过程耗时超过16毫秒,用户就会感觉卡顿、不流畅。
为了避免这种情况,你需要在后台执行类似于这种耗时的操作。对于Android
,这就意味着在不同线程调度工作。而在Flutter,你可以使用Isolate
。
该方法有以下步骤:
- 添加http依赖包
- 使用
http
包发送网络请求 - 将结果解析成
List<Photo>
- 移到
isolate
完成
1.添加http
依赖包
在pubspec.yaml
文件添加依赖包
dependencies:
http: <latest_version>
2.发送网络请求
本例中,使用http.get()
方法从JSONPlaceholder REST API中获取一个包含5000个照片对象列表的Json
大文档。
Future<http.Response> fetchPhotos(http.Client client) async {
return client.get('https://jsonplaceholder.typicode.com/photos');
}
3.解析Json
创建Photo
类
class Photo {
final int id;
final String title;
final String thumbnailUrl;
Photo({this.id, this.title, this.thumbnailUrl});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
id: json['id'] as int,
title: json['title'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
}
}
将请求结果转换成数组
现在,使用以下说明更新fetchPhotos()
函数,以便它返回一个Future<List<Photo>>
:
- 创建一个
parsePhotos()
函数,该函数将请求结果转换为List<Photo>
- 在
fetchPhotos()
函数中调用parsePhotos()
函数
// A function that converts a response body into a List<Photo>.
List<Photo> parsePhotos(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
Future<List<Photo>> fetchPhotos(http.Client client) async {
final response =
await client.get('https://jsonplaceholder.typicode.com/photos');
return parsePhotos(response.body);
}
4.转移到isolate
中完成
如果你在一台很慢的设备中运行fetchPhotos()
函数,你可能会注意到,当应用在解析和转换JSON时会有点卡。这就是‘jank’,而且你会想要摆脱它。
你可以通过调用Flutter提供的computed()
函数在后台的isolate
进行解析和转换来摆脱‘jank’。computed()
会在后台运行耗时操作并将结果返回。在这个例子中,运行的是parsePhotos()
函数。
Future<List<Photo>> 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);
}
5.注意事项
Isolate
通过来回传递信息进行通信,这些信息可以是原始值,例如null
,num
,bool
,double
,或者string
,也可以是简单的对象就像这个例子中的List<Photo>
。
如果你尝试在isolate
之间传递更复杂的对象,比如Future
或http.Response
,你可能会遇到错误。
6.尾记
这是今天在翻官网看到,感觉还不错。不过现在翻完,感觉简单了点,但感觉挺实用的,就像Android中的AsynTask
之类的。但都辛苦翻完了,就当一篇小记录吧。才疏学浅,没有什么深度,还请多指教。
还有这个jank
实在不会翻,有些地方还能领悟,有些地方就完全不懂了。
7.完整代码
import 'dart:async';
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
Future<List<Photo>> 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<Photo>.
List<Photo> parsePhotos(String responseBody) {
final parsed = json.decode(responseBody).cast<Map<String, dynamic>>();
return parsed.map<Photo>((json) => Photo.fromJson(json)).toList();
}
class Photo {
final int albumId;
final int id;
final String title;
final String url;
final String thumbnailUrl;
Photo({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});
factory Photo.fromJson(Map<String, dynamic> json) {
return Photo(
albumId: json['albumId'] as int,
id: json['id'] as int,
title: json['title'] as String,
url: json['url'] as String,
thumbnailUrl: json['thumbnailUrl'] as String,
);
}
}
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
final appTitle = 'Isolate Demo';
return MaterialApp(
title: appTitle,
home: MyHomePage(title: appTitle),
);
}
}
class MyHomePage extends StatelessWidget {
final String title;
MyHomePage({Key key, this.title}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(title),
),
body: FutureBuilder<List<Photo>>(
future: fetchPhotos(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) print(snapshot.error);
return snapshot.hasData
? PhotosList(photos: snapshot.data)
: Center(child: CircularProgressIndicator());
},
),
);
}
}
class PhotosList extends StatelessWidget {
final List<Photo> photos;
PhotosList({Key key, this.photos}) : super(key: key);
@override
Widget build(BuildContext context) {
return GridView.builder(
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisCount: 2,
),
itemCount: photos.length,
itemBuilder: (context, index) {
return Image.network(photos[index].thumbnailUrl);
},
);
}
}
效果图