简体   繁体   中英

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?

1. Use Future

You can use the async version of the 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

In your code, it may not necessarily be the readAsBytes line that is slow. It might be the base 64 encodings of the image. 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. 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 .

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. 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. 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.

For I/O-bound work, declare the function as an async function, and await on long-running tasks inside the function:

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.

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() . 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. 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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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