前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >flutter中的多线程

flutter中的多线程

作者头像
用户1974410
发布2022-09-20 16:46:35
1.4K0
发布2022-09-20 16:46:35
举报
文章被收录于专栏:flutter开发精选

今天我们来学习下flutter中的多线程Isolate的用法。

下面我们会通过如何解析JSON数据来学习isolate的使用,json解析在app中是非常常见的。如果json数据小,在main isolate解析是没有任何问题的,如果数据过大的时候,就会阻塞UI(表现为卡顿和丢帧),所以这时候就会用到Isolate。

这里有两个概念worker isolate和main isolate,相当于多线程了,但不是真正的多线,dart是单线程的。

在本文中,我们将学习通过两种方式解析 JSON,即isolate的两种使用方式:

  • 使用compute()函数
  • 通过spawning an isolate来完成并在完成后调用Isolate.exit()通知worker isolate。

首先我们先定一个需要解析的json格式:

代码语言:javascript
复制
{
  "results": [
    {
      "title": "Flutter Tutorial: Stopwatch App with Custom UI and Animations",
      "url": "https://font.happystudy.ink/posts/1",
      "date": "Dec 6, 2021",
      "contentType": "video"
    },
    {
      "title": "Responsive layouts in Flutter: Split View and Drawer Navigation",
      "url": "https://font.happystudy.ink/posts/2",
      "date": "Nov 26, 2021",
      "contentType": "article"
    },
    {
      "title": "How to create a Flutter GridView with content-sized items",
      "url": "https://font.happystudy.ink/posts/3",
      "date": "Nov 24, 2021",
      "contentType": "article"
    }
  ]
}

这个例子中仅仅展示了3条数据,但实际中可能会有很多数据,假如超过了1M。我们先定义一个解析的类SearchResult.

代码语言:javascript
复制
class SearchResult {
  SearchResult({
    required this.title,
    required this.url,
    required this.date,
  });
  final String title;
  final String url;
  final String date;

  factory SearchResult.fromJson(Map<String, dynamic> data) {
    return SearchResult(
      title: data['title'],
      url: data['url'],
      date: data['date'],
    );
  }
}

使用 isolates解析数据

我们先定义一个解析用的类,如下

代码语言:javascript
复制
import 'dart:convert';

class SearchResultsParser {
  List<SearchResult> _decodeAndParseJson(String encodedJson) {
    final jsonData = jsonDecode(encodedJson);
    final resultsJson = jsonData['results'] as List<dynamic>;
    return resultsJson.map((json) => SearchResult.fromJson(json)).toList();
  }
}

**_decodeAndParseJson()**方法现在不是异步的,但是如果现在数据量很大时,这个解析方法将耗费很长时间。

那么我们现在如何让这个函数在后台运行,不阻塞我们的UI呢?

现在先用我们的第一种方法compute():

代码语言:javascript
复制
import 'dart:convert';
import 'package:flutter/foundation.dart';

class SearchResultsParser {
  Future<List<SearchResult>> parseInBackground(String encodedJson) {
    // compute spawns an isolate, runs a callback on that isolate, and returns a Future with the result
    return compute(_decodeAndParseJson, encodedJson);
  }

  List<SearchResult> _decodeAndParseJson(String encodedJson) {
    final jsonData = jsonDecode(encodedJson);
    final resultsJson = jsonData['results'] as List<dynamic>;
    return resultsJson.map((json) => SearchResult.fromJson(json)).toList();
  }
}

我们使用了compute()方法,这样就将我们的解析方法放到了worker isolate, 现在再运行我们的程序,就不会出现卡顿的现象了!

compute是dart中为我们封装好的快速使用的方法。下面我们再试试另外一种更加灵活的使用方式。

使用Isolate.exit()快速实现

compute虽然使用简单,但有一些问题,Flutter 2.8以前compute耗时会长一些,所以compute会比实际解析耗时会长那么一点点。

我们现在学习如何自己使用 isolate API:

代码语言:javascript
复制
class SearchResultsParser {
  // 1. pass the encoded json as a constructor argument
  SearchResultsParser(this.encodedJson);
  final String encodedJson;

  // 2. public method that does the parsing in the background
  Future<List<SearchResult>> parseInBackground() async {
    // create a port
    final p = ReceivePort();
    // spawn the isolate and wait for it to complete
    await Isolate.spawn(_decodeAndParseJson, p.sendPort);
    // get and return the result data
    return await p.first;
  }

  // 3. json parsing
  Future<void> _decodeAndParseJson(SendPort p) async {
    // decode and parse the json
    final jsonData = jsonDecode(encodedJson);
    final resultsJson = jsonData['results'] as List<dynamic>;
    final results = resultsJson.map((json) => SearchResult.fromJson(json)).toList();
    // return the result data via Isolate.exit()
    Isolate.exit(p, results);
  }
}

关键代码第2步和第3步,

  • 我们使用Isolate.spawn()显式地创建一个新的Isolate,注意是异步的,使用了await
  • _decodeAndParseJson()使用Isolate.exit() ,通知解析完成并返回了结果results
  • Isolate.spawn()只能接受一个参数,所以encodedJson只能通过构造函数来传递示例变量。

对比这两种方法,我们可以看出compute更加简便快捷。

什么情况下我们需要让我们的代码在background

我们可以通过以下方法来测试下:

  • 以profile模式在低配置的设备上运行
  • 调整数据的大小,看看我们的UI是否卡顿或者丢帧

这样做会耗费很多时间,一般来说如果解析json数据超过10KB,我们就需要使用background了,毕竟1行代码就可以搞定。

代码语言:javascript
复制
compute(_decodeAndParseJson, encodedJson)

扩展:networking的代码需要 worker isolate吗

到目前为止,我们只是把json解析的代码放到了worker isolate,那么 networking的代码需要放入吗?

代码语言:javascript
复制
import 'package:http/http.dart' as http;

class APIClient {
  Future<List<SearchResult>> downloadAndParseJson() async {
    // get the data from the network
    final response = await http
        .get(Uri.parse('https://codewithandrea.com/search/search.json'));
    if (response.statusCode == 200) {
      // on success, parse the JSON in the response body
      final parser = SearchResultsParser();
      return parser.parseInBackground(response.body);
    } else {
      // on failure, throw an exception
      throw Exception('Failed to load json');
    }
  }
}

其实,nerworkingfile IO已经是在一个**separate task runner.**当IO操作完成的时候,就会返回结果到main isolate。

也就是说我们能够安全的使用flutter中IO操作相关的API,dart已经都给我们封装好了。

结论

使用worker isolate, 我们使用compute就能快速实现,在flutter2.8(Dart 2.15)我们compute的速度已经得到优化,简单场景无需我们再自定义使用Isolate了。

相关概念介绍

如果你想学习更多关于Isolate的东西,推荐以下阅读:

  • Concurrency in Dart(https://dart.dev/guides/language/concurrency)

也有一些优秀的外国视频可以参考:

  • Async vs Isolates | Decoding Flutter https://youtu.be/5AxWC49ZMzs
  • Isolates and Event Loops - Flutter in Focus https://youtu.be/vl_AaCgudcY

关于 Flutter 2.8 and Dart 2.15 最新公告:

  • What’s New in Flutter 2.8 https://medium.com/flutter/whats-new-in-flutter-2-8-d085b763d181
  • Announcing Dart 2.15 https://medium.com/dartlang/dart-2-15-7e7a598e508a

StackOverflow 多线程的解释 how Dart manages IO operations under the hood:

What thread / isolate does flutter run IO operations on? https://stackoverflow.com/questions/56906744/what-thread-isolate-does-flutter-run-io-operations-on

-------------------------少年别走,交个朋友--------------------------

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-01-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 flutter开发精选 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使用 isolates解析数据
  • 使用Isolate.exit()快速实现
  • 什么情况下我们需要让我们的代码在background呢
  • 扩展:networking的代码需要 worker isolate吗
  • 结论
  • 相关概念介绍
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档