Flutter can implement async, awite, and Futrue asynchronously, but these are essentially the same as the Handle queue. Besides, the message queue runs in the UI thread. If you really have any time-consuming operations there, there will be no problem. So do you want to start new threads or do you want to start new threads? Let’s talk about how to communicate between new Isolate and Isolate, but let’s look at the simple function compute


Creation of the ISOLates and communication between them

Each of the isolates is memory isolated, and each of the isolates executes messages in queues. This is a characteristic of the ISOLates. The concepts between languages are basically similar, but the implementation is slightly different. However, due to the similarity in concept, the same requirements will naturally arise. Dart provides a port for communication between isolates

Ports come in pairs and are divided into: ReceivePort Receives port and SendPort sends port. SendPort can generate SendPort by itself. As long as SendPort is passed to the other party, the permission from SendPort->receivePort can be implemented. Of course, this is a single item, two-way communication is also the same, the opposite side of their own SendPort to pass it

var receivePort = ReceivePort();  
var sendPort = receivePort.sendPort;
Copy the code

The APIS for creating Isolate are: Await Isolate. Spawn (Function,SendPort), because it is an asynchronous operation, add await, Function is a method, it is the core method that the new thread executes, same meaning as run method, SendPort is the communicator we want to send to the other side

var anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);
Copy the code

Listen We can get data from the Isolate. All data types can be transmitted between the Isolate without making any marks. It must be realized by the underlying layer

receivePort.listen((date) {
    print("Isolate 1 Receives messages: data = $date");
});
Copy the code

Receiveport. first can wait to get the first return result, but cannot be written with receivePort.listen

final sendPort = await receivePort.first as SendPort;
Copy the code

Shutting down the Isolate is straightforward. Call the kill method on the Isolate that it controls within the Main Isolate

void stop(){
print("kill isolate"); isolate? .kill(priority: Isolate.immediate); isolate =null;
}
Copy the code

No nonsense, the above is very simple, put the principle we all understand, the following direct look at the code:


Isolate one-way communication

Isolate the code:

import 'dart:isolate';

var anotherIsolate;
var value = "Now Thread!";

void startOtherIsolate() async {
  var receivePort = ReceivePort();

  anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);

  receivePort.listen((date) {
    print("Isolate 1 accepts messages: data = $date, value = $value");
  });
}

void otherIsolateInit(SendPort sendPort) async {
  value = "Other Thread!";
  sendPort.send("BB");
}
Copy the code

Run code:

import 'DartLib.dart';

void main(){
  startOtherIsolate();
}
Copy the code

Results:

Isolate 1 Receives messages: data = BB, value = Now Thread!Copy the code

Isolate two-way communication

Isolate the code:

import 'dart:isolate';

var anotherIsolate;
var value = "Now Thread!";

void startOtherIsolate() async {
  var receivePort = ReceivePort();
  var sendPort;

  anotherIsolate = await Isolate.spawn(otherIsolateInit, receivePort.sendPort);

  receivePort.listen((date) {
    if (date is SendPort) {
      sendPort = date as SendPort;
      print("Two-way communication established successfully");
      return;
    }
    print("Isolate 1 Receives messages: data = $date");
    sendPort.send("AA");
  });
}

void otherIsolateInit(SendPort sendPort) async {
  value = "Other Thread!";

  var receivePort = ReceivePort();
  print("Isolate 2 received a port from Isolate 1 and attempted to establish two-way communication with Isolate 1");

  receivePort.listen((date) {
    print("Isolate 2 Receives messages: data = $date");
  });

  sendPort.send(receivePort.sendPort);

  for (var index = 0; index < 10; index++) {
    sendPort.send("BB$index"); }}Copy the code

Run code:

import 'DartLib.dart';

void main(){
  startOtherIsolate();
}
Copy the code

Running results:

Isolate 2Received from the Isolate1Port, try to establish the same as the Isolate1The two - way communication has been established1Receiving messages: data = BB0 Isolate1Receive messages: data = BB1 Isolate1Receive messages: data = BB2 Isolate1Receive messages: data = BB3 Isolate1Receiving messages: Data = BB4 Isolate1Receive messages: data = BB5 Isolate1Receive messages: data = BB6 Isolate1Receive messages: data = BB7 Isolate1Receive messages: data = BB8 Isolate1Receiving messages: Data = BB9 Isolate2Receiving messages: Data = AA Isolate2Receiving messages: Data = AA Isolate2Receiving messages: Data = AA Isolate2Receiving messages: Data = AA Isolate2Receiving messages: Data = AA Isolate2Receiving messages: Data = AA Isolate2Receiving messages: Data = AA Isolate2Receiving messages: Data = AA Isolate2Receiving messages: Data = AA Isolate2Receive message: data = AACopy the code

System API: computer function

It’s a bit of a hassle to create a new Isoalte and implement the communication. The code is basically repetitive from a encapsulation perspective, so Google provides an API to do this: compute method

The compute method is provided by Flutter (remember not Dart). Compute creates an Isolate inside of Flutter and returns the result of the computation

Compute (function, value) compute takes two parameters, the first one is the core method of the new thread, the second one is the parameter passed from the new thread. Function Function parameters should be designed to match values

Compute method in the import ‘package: flutter/foundation. The dart’ inside the package

Here's an example:

import 'dart:io';
import 'dart:isolate';
import 'package:flutter/foundation.dart';

void newTask() async {
  print("Start time calculation, current ISOLATE =${Isolate.current.toString()}");
  var result = await compute(getName, "name");
  print(result);
}

String getName(String name) {
  print("Getting results... , current ISOLATE =${Isolate.current.toString()}");
  sleep(Duration(seconds: 2));
  return "Name";
}
Copy the code

Run code:

newTask();
Copy the code
I/ FLUTTER (24384): Start time calculation, current ISOLATE = Instance of 'ISOLATE 'I /flutter (24384): Obtaining results... , current ISOLATE = Instance of 'ISOLATE 'I/FLUTTER (24384): NameCopy the code

Compute function

Compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute: compute

// Compute takes two parameters, as we've already seen
Future<R> compute<Q, R>(ComputeCallback<Q, R> callback, Q message, { String debugLabel }) async {
  // Call callback.toString() if the debugLabel is emptyprofile(() { debugLabel ?? = callback.toString(); });final Flow flow = Flow.begin();
  Timeline.startSync('$debugLabel: start', flow: flow);
  final ReceivePort resultPort = ReceivePort();
  Timeline.finishSync();
  // Create the ISOLATE. This is the same as the previous method of creating the ISOLATE
  // One more thing, the arguments passed here are classes wrapped in _IsolateConfiguration
  final Isolate isolate = await Isolate.spawn<_IsolateConfiguration<Q, R>>(
    _spawn,
    _IsolateConfiguration<Q, R>(
      callback,
      message,
      resultPort.sendPort,
      debugLabel,
      flow.id,
    ),
    errorsAreFatal: true,
    onExit: resultPort.sendPort,
  );
  final R result = await resultPort.first;
  Timeline.startSync('$debugLabel: end', flow: Flow.end(flow.id));
  resultPort.close();
  isolate.kill();
  Timeline.finishSync();
  return result;
}
Copy the code

Isolate Application Scenario

Although THE Isolate is good, it can also be used in appropriate scenarios. Abuse of the Isolate is not recommended. You should use the event loop mechanism of Dart to process asynchronous tasks as much as possible, so as to make full use of the advantages of Dart

So when should you use Future and when should you use Isolate? One of the easiest ways to do this is to choose based on the average time of certain tasks:

  • Methods that execute in milliseconds or tens of milliseconds should use the Future
  • If a task takes a few hundred milliseconds or more, it is recommended to create a separate Isolate

Beyond that, there are a few scenarios to look at

  • JSON decode
  • encryption
  • Image processing: for example, cropping
  • Network request: load resources, images