Flutter

【译】Flutter :Parse JSON in the ba

2019-08-21  本文已影响0人  违规ID9633

原文为Flutter官方的Cookbook:Parse JSON in the background

通常,Dart app 会在一个线程内完成所有工作。许多情况下,这个模型简化了编码,速度足够快而不会导致应用程序性能差或动画断断续续,通常称为“jank”。

然而,你可能需要执行一些耗时的操作,例如:解析一个非常大的Json文件。如果这个过程耗时超过16毫秒,用户就会感觉卡顿、不流畅。

为了避免这种情况,你需要在后台执行类似于这种耗时的操作。对于Android,这就意味着在不同线程调度工作。而在Flutter,你可以使用Isolate

该方法有以下步骤:

  1. 添加http依赖包
  2. 使用http包发送网络请求
  3. 将结果解析成List<Photo>
  4. 移到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>>:

  1. 创建一个parsePhotos()函数,该函数将请求结果转换为List<Photo>
  2. 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通过来回传递信息进行通信,这些信息可以是原始值,例如nullnum,bool,double,或者string,也可以是简单的对象就像这个例子中的List<Photo>

如果你尝试在isolate之间传递更复杂的对象,比如Futurehttp.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);
      },
    );
  }
}
效果图
上一篇下一篇

猜你喜欢

热点阅读