📜  Flutter – 避免 Jank

📅  最后修改于: 2021-09-23 06:38:36             🧑  作者: Mango

解析大型 JSON 文件可能会导致应用程序性能不佳和关闭动画。如果解析和计算大型 JSON 文件的时间超过 16 毫秒,用户就会遇到 Jank。 Dart默认使用单线程来执行这些任务,虽然对于较便宜的计算来说简单快速,但在计算量很大时它会失败。

为了避免这些卡顿,可以使用隔离在后台的不同线程中执行所有计算。在本文中,我们将探索在后台解析 JSON 的过程。为此,请执行以下步骤:

  1. 导入http包。
  2. 使用 http 包发出网络请求
  3. 将响应更改为列表形式的dart对象
  4. 将这项工作移至不同的隔离区

导入 http 包:

要安装 http 包,请在命令提示符中使用以下命令:

pub get

或者,如果您使用的是flutter cmd,请使用以下命令:

flutter pub get

安装完成后,将依赖添加到 pubsec.yml 文件中,如下所示:

import 'package:http/http.dart' as http;

请求数据:

我们可以使用http.get() 方法从 JSONPlaceholder REST API 获取 5000 张图像的示例数据,如下所示:

Dart
Future fetchPhotos(http.Client client) async {
  return client.get('https://jsonplaceholder.typicode.com/photos');
}


Dart
class Photo_list {
  final int photo_id;
  final String photo_title;
  final String photo_thumbnailUrl;
  
  Photo({this.photo_id, this.photo_title, this.photo_thumbnailUrl});
  
  factory Photo_list.fromJson(Map json) {
    return Photo_list(
      photo_id: json['id'] as int,
      photo_title: json['title'] as String,
      photo_thumbnailUrl: json['thumbnailUrl'] as String,
    );
  }
}


Dart
ListPhotos_parser(String responseBody) {
  final parsed = json.decode(responseBody).cast>();
  
  return parsed.map((json) => Photo.fromJson(json)).toList();
}
  
Future> getPhotos(http.Client client) async {
  final response =
      await client.get('https://jsonplaceholder.typicode.com/photos');
  
  return Photos_parser(response.body);
}


Dart
Future> getPhotos(http.Client client) async {
  final response =
      await client.get('https://jsonplaceholder.typicode.com/photos');
  return compute(parsePhotos, response.body);
}


Dart
import 'dart:async';
import 'dart:convert';
  
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
  
Future> getPhotos(http.Client client) async {
  final response =
  await client.get('https://jsonplaceholder.typicode.com/photos');
  
  // run Photos_parser in a separate isolate.
  return compute(Photos_parser, response.body);
}
  
List Photos_parser(String responseBody) {
  final parsed = jsonDecode(responseBody).cast>();
  
  return parsed.map((json) => Photo.fromJson(json)).toList();
}
  
class Photo_list{
  final int Photo_albumId;
  final int Photo_id;
  final String Photo_title;
  final String Photo_url;
  final String Photo_thumbnailUrl;
  
  Photo_list({this.Photo_albumId, this.Photo_id, this.Photo_title, this.Photo_url, this.Photo_thumbnailUrl});
  
  factory Photo_list.fromJson(Map json) {
    return Photo_list(
      Photo_albumId: json['albumId'] as int,
      Photo_id: json['id'] as int,
      Photo_title: json['title'] as String,
      Photo_url: json['url'] as String,
      Photo_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 HomePage extends StatelessWidget {
  final String title;
  
  HomePage({Key key, this.title}) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: FutureBuilder>(
        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 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].Photo_thumbnailUrl);
      },
    );
  }
}


解析和转换数据:

我们现在有 5000 张从http.response接收到的 JSON 格式的照片,这些照片需要解析并转换为Dart对象列表。要做 show 首先创建一个 Photo 类,如下所示。

在这里,我们将创建一个包含 JSON 数据的 Photo 类,如下所示:

Dart

class Photo_list {
  final int photo_id;
  final String photo_title;
  final String photo_thumbnailUrl;
  
  Photo({this.photo_id, this.photo_title, this.photo_thumbnailUrl});
  
  factory Photo_list.fromJson(Map json) {
    return Photo_list(
      photo_id: json['id'] as int,
      photo_title: json['title'] as String,
      photo_thumbnailUrl: json['thumbnailUrl'] as String,
    );
  }
}

现在通过以下步骤更新getPhoto()函数以返回 Future> :

  1. 使用 Photos_parser()函数将响应正文转换为 List
  2. 在 getPhotos()函数中使用 Photos_parser()函数。

Dart

ListPhotos_parser(String responseBody) {
  final parsed = json.decode(responseBody).cast>();
  
  return parsed.map((json) => Photo.fromJson(json)).toList();
}
  
Future> getPhotos(http.Client client) async {
  final response =
      await client.get('https://jsonplaceholder.typicode.com/photos');
  
  return Photos_parser(response.body);
}

将工作转移到不同的隔离区:

compute()函数可用于将工作移动到单独的隔离区,在那里它将在后台进行解析和转换。它在后台隔离中运行昂贵的函数并返回结果。

Dart

Future> getPhotos(http.Client client) async {
  final response =
      await client.get('https://jsonplaceholder.typicode.com/photos');
  return compute(parsePhotos, response.body);
}

完整的源代码:

Dart

import 'dart:async';
import 'dart:convert';
  
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
  
Future> getPhotos(http.Client client) async {
  final response =
  await client.get('https://jsonplaceholder.typicode.com/photos');
  
  // run Photos_parser in a separate isolate.
  return compute(Photos_parser, response.body);
}
  
List Photos_parser(String responseBody) {
  final parsed = jsonDecode(responseBody).cast>();
  
  return parsed.map((json) => Photo.fromJson(json)).toList();
}
  
class Photo_list{
  final int Photo_albumId;
  final int Photo_id;
  final String Photo_title;
  final String Photo_url;
  final String Photo_thumbnailUrl;
  
  Photo_list({this.Photo_albumId, this.Photo_id, this.Photo_title, this.Photo_url, this.Photo_thumbnailUrl});
  
  factory Photo_list.fromJson(Map json) {
    return Photo_list(
      Photo_albumId: json['albumId'] as int,
      Photo_id: json['id'] as int,
      Photo_title: json['title'] as String,
      Photo_url: json['url'] as String,
      Photo_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 HomePage extends StatelessWidget {
  final String title;
  
  HomePage({Key key, this.title}) : super(key: key);
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(title),
      ),
      body: FutureBuilder>(
        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 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].Photo_thumbnailUrl);
      },
    );
  }
}

输出:

想要一个更快节奏和更具竞争力的环境来学习 Android 的基础知识吗?
单击此处前往由我们的专家精心策划的指南,旨在让您立即做好行业准备!