Post

探究Flutter Isolate

前情提要

當碰到跑大量的演算法,或是下載較大的檔案,都會碰到UI整個卡住的情形,那這到底是怎麼回是?這要從底層開始說起。

首先要知道,Dart是一種單執行緒語言,這意味著在運行時,都會依序處理完佇列中每個工作(Event Loop)。

打開Observatory debugger看一下,當我們在運行時(run main.dart),可以發現一定會有一個叫main的Isolate。

什麼是Isolate

而Isolate作為Dart的Process,是單一執行緒,而每個Isolate記憶體是不共享的,當他們要傳達訊息時,需要透過socket的方式進行,既然Isolate會依序執行工作,那為甚麼還會有卡住個問題呢?

非同步產生的問題

下方是一段讀取檔案的code,我們拿讀取檔案的部分為例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
void main() async {
  // Read some data.
  final fileData = await _readFileAsync();
  final jsonData = jsonDecode(fileData);

  // Use that data.
  print('Number of JSON keys: ${jsonData.length}');
}

Future<String> _readFileAsync() async {
  final file = File(filename);
  final contents = await file.readAsString();
  return contents.trim();
}

在處理非同步的問題,會運用到Async await Future的方式,這邊Dart跑到await _readFileAsync,Dart會先跳開去處理File I/O(這邊的非同步)直到檔案資料取完後,Dart才會繼續執行下一段程式。

而一般如果檔案不大,每段event結束後都可以快速的執行下一段event。

為了達到畫面保持順暢,Flutter為每個事件(event)添加每秒60次的”paint frame”(所謂的60Hz)。 當檔案太大時,如果使用非同步操作,會導致後續執行的event太晚接收,造成UI卡住,嚴重了話可能造成程式看起來沒有回應(沒有快速執行下一個event)。

如何解決

既然我們都知道,非同步(異步)的操作實際上只是等待某個某段程式直到做出回應,那我們要做到的就是平行處理(concurrency),簡單來說就是創建另一個Isolate去執行那一段處理時間較長的工作。

創建方法

運用dart:isolate

  • Isolate.spawn(創建Isolate)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    import 'dart:isolate';
    
    void main() {
      Isolate.spawn(isolateFun, '隔離產生');
    }
    
    void isolateFun(message) {
      print(message);
    }
    
  • compute(Spawn的包裝版): 單純做計算用可以直接使用

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    
    import 'package:flutter/foundation.dart';
    
    Future<int> computedFunction() async {
      return compute(computeMultiplyFunction, 100);
    }
    
    // the function must be a top-level function or a static method
    static int computeMultiplyFunction(int num) {
      return num * num;
    }
    

結語

經過這個事件,可以大致了解Dart在底層是怎麼運作的,而這邊可以嚐試使用flutter_isolate,這個的好處是他可以自己設工作負載,幫助你的應用程式不卡卡。

This post is licensed under CC BY 4.0 by the author.