深入瞭解Flutter的isolate(4) — 使用Compute寫isolates

小德_koudle發表於2019-01-13

0x00 前言

前面講了如何建立isolate,這篇文章講建立isolate的另一種方法。

0x01 使用isolates的方法

使用isolates的方法種:

  1. 高階API:Compute函式 (用起來方便)
  2. 低階API:ReceivePort

0x02 Compute函式

Compute函式對isolate的建立和底層的訊息傳遞進行了封裝,使得我們不必關係底層的實現,只需要關注功能實現。

首先我們需要:

  1. 一個函式:必須是頂級函式或靜態函式
  2. 一個引數:這個引數是上面的函式定義入參(函式沒有引數的話就沒有)

比如,還是計算斐波那契數列:

void main() async{ 
//呼叫compute函式,compute函式的引數就是想要在isolate裡執行的函式,和這個函式需要的引數 print( await compute(syncFibonacci, 20));
runApp(MyApp());

}int syncFibonacci(int n){
return n <
2 ? n : syncFibonacci(n-2) + syncFibonacci(n-1);

}複製程式碼

執行後的結果如下:

flutter: 6765複製程式碼

是不是很簡單,接下來看下compute函式的原始碼,這裡的程式碼有點複雜,會把分析的新增到程式碼的註釋裡,首先介紹一個compute函式裡用到的函式別名:

ComputeCallback<
Q, R>
定義如下:

// Q R是泛型,ComputeCallback是一個有引數Q,返回值為R的函式typedef ComputeCallback<
Q, R>
= R Function(Q message);
複製程式碼

正式看原始碼:

//compute函式 必選引數兩個,已經講過了Future<
R>
compute<
Q, R>
(ComputeCallback<
Q, R>
callback, Q message, {
String debugLabel
}) async {
//如果是在profile模式下,debugLabel為空的話,就取callback.toString() profile(() {
debugLabel ??= callback.toString();

});
final Flow flow = Flow.begin();
Timeline.startSync('$debugLabel: start', flow: flow);
final ReceivePort resultPort = ReceivePort();
Timeline.finishSync();
//建立isolate,這個和前面講的建立isolate的方法一致 //還有一個,這裡傳過去的引數是用_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;

}@immutableclass _IsolateConfiguration<
Q, R>
{
const _IsolateConfiguration( this.callback, this.message, this.resultPort, this.debugLabel, this.flowId, );
final ComputeCallback<
Q, R>
callback;
final Q message;
final SendPort resultPort;
final String debugLabel;
final int flowId;
R apply() =>
callback(message);

}void _spawn<
Q, R>
(_IsolateConfiguration<
Q, R>
configuration) {
R result;
Timeline.timeSync( '${configuration.debugLabel
}'
, () {
result = configuration.apply();

}, flow: Flow.step(configuration.flowId), );
Timeline.timeSync( '${configuration.debugLabel
}: returning result'
, () {
configuration.resultPort.send(result);

}, flow: Flow.step(configuration.flowId), );

}複製程式碼

0x03 ReceivePort

import 'dart:async';
import 'dart:io';
import 'dart:isolate';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
//一個普普通通的Flutter應用的入口//main函式這裡有async關鍵字,是因為建立的isolate是非同步的void main() async{
runApp(MyApp());
//asyncFibonacci函式裡會建立一個isolate,並返回執行結果 print(await asyncFibonacci(20));

}//這裡以計算斐波那契數列為例,返回的值是Future,因為是非同步的Future<
dynamic>
asyncFibonacci(int n) async{
//首先建立一個ReceivePort,為什麼要建立這個? //因為建立isolate所需的引數,必須要有SendPort,SendPort需要ReceivePort來建立 final response = new ReceivePort();
//開始建立isolate,Isolate.spawn函式是isolate.dart裡的程式碼,_isolate是我們自己實現的函式 //_isolate是建立isolate必須要的引數。 await Isolate.spawn(_isolate,response.sendPort);
//獲取sendPort來傳送資料 final sendPort = await response.first as SendPort;
//接收訊息的ReceivePort final answer = new ReceivePort();
//傳送資料 sendPort.send([n,answer.sendPort]);
//獲得資料並返回 return answer.first;

}//建立isolate必須要的引數void _isolate(SendPort initialReplyTo){
final port = new ReceivePort();
//繫結 initialReplyTo.send(port.sendPort);
//監聽 port.listen((message){
//獲取資料並解析 final data = message[0] as int;
final send = message[1] as SendPort;
//返回結果 send.send(syncFibonacci(data));

});

}int syncFibonacci(int n){
return n <
2 ? n : syncFibonacci(n-2) + syncFibonacci(n-1);

}複製程式碼

來源:https://juejin.im/post/5c3a06f56fb9a049d37f54f4

相關文章