简体   繁体   English

Flutter:如何在 dart 的后台线程上运行 function

[英]Flutter: How can I run a function on background thread in dart

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);

}

The readAsBytesSync() method works fine on Android but too slow in iOS. So how can I move this code to a new background thread? readAsBytesSync()方法在 Android 上运行良好,但在 iOS 上运行速度太慢。那么如何将此代码移动到新的后台线程?

1. Use Future 1. 使用未来

You can use the async version of the readAsBytes .您可以使用readAsBytes的异步版本。

So instead of:所以而不是:

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

You could have:你可以有:

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

2. UseIsolate 2. 使用隔离

In your code, it may not necessarily be the readAsBytes line that is slow.在您的代码中,它可能不一定是慢的readAsBytes行。 It might be the base 64 encodings of the image.它可能是图像的 base 64 编码。 If that's the case, you could put the entire computation into a separate isolate.如果是这种情况,您可以将整个计算放在一个单独的隔离中。 There is a convenient method compute that you can use.您可以使用一种方便的计算方法。

// 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);
}

In addition, it is worth mentioning that SharedPreferences (for Android, aka, NSUserDefaults on iOS) is designed to store small user settings.此外,值得一提的是SharedPreferences (对于 Android,也就是 iOS 上的NSUserDefaults )旨在存储小型用户设置。 It is not designed for storing the images, which could be megabytes big.它不是为存储图像而设计的,图像可能有几兆字节大。 It is better to copy the image file into the app's document folder, and only store the filename in the SharedPreferences .最好将图像文件复制到应用程序的文档文件夹中,并且只将文件名存储在SharedPreferences

Since Flutter is single-threaded and runs an event loop (like Node.js), you don't have to worry about thread management or spawning background threads.由于 Flutter 是单线程的并运行一个事件循环(如 Node.js),因此您不必担心线程管理或产生后台线程。 If you're doing I/O-bound work, such as disk access or a network call, then you can safely use async/await and you're done.如果您正在执行 I/O 绑定工作,例如磁盘访问或网络调用,那么您可以安全地使用async/await并完成。 If, on the other hand, you need to do computationally intensive work that keeps the CPU busy, you want to move it to an Isolate to avoid blocking the event loop.另一方面,如果您需要进行计算密集型工作以保持 CPU 忙碌,您希望将其移至Isolate以避免阻塞事件循环。

For I/O-bound work, declare the function as an async function, and await on long-running tasks inside the function:对于 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);
  });
}

This is how you typically do network or database calls, which are both I/O operations.这就是您通常执行网络或数据库调用的方式,它们都是 I/O 操作。

Isolates are separate execution threads that do not share any memory with the main execution memory heap.隔离是独立的执行线程,不与主执行内存堆共享任何内存。 This means you can't access variables from the main thread, or update your UI by calling setState() .这意味着您无法从主线程访问变量,也无法通过调用setState()更新您的 UI。 Isolates are true to their name, and cannot share memory (in the form of static fields, for example).隔离正如其名,不能共享内存(例如,以静态字段的形式)。

Here, dataLoader() is the Isolate that runs in its own separate execution thread.这里, dataLoader()是运行在自己独立执行线程中的Isolate In the isolate you can perform more CPU intensive processing (parsing a big JSON, for example), or perform computationally intensive math, such as encryption or signal processing.在隔离中,您可以执行更多 CPU 密集型处理(例如解析大 JSON),或执行计算密集型数学,例如加密或信号处理。

You can run the full example below:您可以运行下面的完整示例:

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;
  }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM