【问题标题】:Flutter: How can I run a function on background thread in dartFlutter:如何在 dart 的后台线程上运行函数
【发布时间】:2019-01-07 20:37:11
【问题描述】:
Future getImage() async {
 var image = await ImagePicker.pickImage(source: ImageSource.camera);
 setState(() {
   _image = image;
   print("IMG:" + _image.toString());
 });
 setPrefs() ;
}

Future setPrefs() async {
 _base64 = base64.encode(_image.readAsBytesSync());
 print(_base64);
 final prefs = await SharedPreferences.getInstance();
 prefs.setString(IMAGE_KEY, _base64);

}

readAsBytesSync() 方法在 Android 上运行良好,但在 iOS 上太慢了。那么如何将这段代码移动到新的后台线程呢?

【问题讨论】:

标签: flutter dart asynchronous async-await background-thread


【解决方案1】:

1.使用未来

您可以使用readAsBytes 的异步版本。

所以而不是:

final imageData = _image.readAsBytesSync();
_base64 = base64.encode(imageData);

你可以:

final imageData = await _image.readAsBytes();
_base64 = base64.encode(imageData);

2。使用Isolate

在您的代码中,速度可能不一定是 readAsBytes 行。它可能是图像的 base 64 编码。如果是这种情况,您可以将整个计算放入单独的隔离中。有一个方便的方法compute可以使用。

// This needs to be a global function
encodeImage(File imageFile) {
    return base64.encodeimageFile.readAsBytesSync());
}
Future setPrefs() async {
 _base64 = await compute(encodeImage, _image);
 print(_base64);
 final prefs = await SharedPreferences.getInstance();
 prefs.setString(IMAGE_KEY, _base64);
}

此外,值得一提的是SharedPreferences(对于Android,又名,在iOS 上为NSUserDefaults)旨在存储小型用户设置。它不是为存储可能是兆字节大的图像而设计的。最好将图片文件复制到应用的文件夹中,文件名只存放在SharedPreferences中。

【讨论】:

  • 非常感谢@Yuchen!!!到处寻找解决方案,您的第一个 readAsBytes 解决方案救了我的培根!!
【解决方案2】:

由于 Flutter 是单线程并运行事件循环(如 Node.js),因此您不必担心线程管理或产生后台线程。如果您正在执行 I/O 密集型工作,例如磁盘访问或网络调用,那么您可以安全地使用 async/await 并完成。另一方面,如果您需要进行计算密集型工作以使 CPU 保持忙碌,您希望将其移至 Isolate 以避免阻塞事件循环。

对于 I/O 密集型工作,将函数声明为 async 函数,并在函数内长时间运行的任务上声明 await

loadData() async {
  String dataURL = "https://jsonplaceholder.typicode.com/posts";
  http.Response response = await http.get(dataURL);
  setState(() {
    widgets = jsonDecode(response.body);
  });
}

这就是您通常执行网络或数据库调用的方式,它们都是 I/O 操作。

隔离是独立的执行线程,不与主执行内存堆共享任何内存。这意味着你不能从主线程访问变量,或者通过调用setState() 来更新你的 UI。隔离区名副其实,不能共享内存(例如,以静态字段的形式)。

这里,dataLoader() 是在自己的单独执行线程中运行的Isolate。在隔离中,您可以执行更多 CPU 密集型处理(例如解析大型 JSON),或执行计算密集型数学运算,例如加密或信号处理。

您可以运行下面的完整示例:

import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'dart:async';
import 'dart:isolate';

void main() {
  runApp(SampleApp());
}

class SampleApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Sample App',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: SampleAppPage(),
    );
  }
}

class SampleAppPage extends StatefulWidget {
  SampleAppPage({Key key}) : super(key: key);

  @override
  _SampleAppPageState createState() => _SampleAppPageState();
}

class _SampleAppPageState extends State<SampleAppPage> {
  List widgets = [];

  @override
  void initState() {
    super.initState();
    loadData();
  }

  showLoadingDialog() {
    if (widgets.length == 0) {
      return true;
    }

    return false;
  }

  getBody() {
    if (showLoadingDialog()) {
      return getProgressDialog();
    } else {
      return getListView();
    }
  }

  getProgressDialog() {
    return Center(child: CircularProgressIndicator());
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        appBar: AppBar(
          title: Text("Sample App"),
        ),
        body: getBody());
  }

  ListView getListView() => ListView.builder(
      itemCount: widgets.length,
      itemBuilder: (BuildContext context, int position) {
        return getRow(position);
      });

  Widget getRow(int i) {
    return Padding(
      padding: EdgeInsets.all(10.0),
      child: Text("Row ${widgets[i]["title"]}"),
    );
  }

  loadData() async {
    ReceivePort receivePort = ReceivePort();
    await Isolate.spawn(dataLoader, receivePort.sendPort);

    // The 'echo' isolate sends its SendPort as the first message
    SendPort sendPort = await receivePort.first;

    List msg = await sendReceive(
      sendPort,
      "https://jsonplaceholder.typicode.com/posts",
    );

    setState(() {
      widgets = msg;
    });
  }

// the entry point for the isolate
  static dataLoader(SendPort sendPort) async {
    // Open the ReceivePort for incoming messages.
    ReceivePort port = ReceivePort();

    // Notify any other isolates what port this isolate listens to.
    sendPort.send(port.sendPort);

    await for (var msg in port) {
      String data = msg[0];
      SendPort replyTo = msg[1];

      String dataURL = data;
      http.Response response = await http.get(dataURL);
      // Lots of JSON to parse
      replyTo.send(jsonDecode(response.body));
    }
  }

  Future sendReceive(SendPort port, msg) {
    ReceivePort response = ReceivePort();
    port.send([msg, response.sendPort]);
    return response.first;
  }
}

【讨论】:

  • 谢谢,我不知道我们可以使用流在Isolates 通过ReceiveportSendport 之间进行通信。但是,对于像我这样的新手来说,您的代码有点难以理解。幸运的是,我找到了一个 blog post,它解释了这里正在做什么。
猜你喜欢
  • 2021-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-08-30
  • 1970-01-01
  • 2017-02-15
相关资源
最近更新 更多