Isolates 在 Flutter 3.7 & Dart 2.19 的升級,你該知道一下!

Yii Chen
Flutter Formosa
Published in
8 min readMar 24, 2023

--

Isolate is a good friend

隔離在 Flutter 是個很重要的角色,讓我們可以藉由它處理一些繁重的任務,例如:文字解析、圖像處理或是大量的資料存取,適當的使用可以避免 Main isolate 的阻塞,讓 APP 在運行時更為順暢。

本篇告訴你在近期的 Flutter 與 Dart 改版後,Isolate成長了多少, 有哪些新 API 和功能,快速了解一下吧。

1. RootIsolateToken

Flutter 3.7 開始,可以在其他 background isolates 使用任何套件(ex: SharedPreferences)以及 Platform Channel。

情境:A 啟動一個 background isolate,從 Firebase Cloud Store 下載 8k 圖像,將它壓縮到所需的導出大小,接著將圖像保存到手機相簿,最後在完成時發布本地通知

下載、壓縮圖像、發送通知都使用 isolate 在背景處理,可以確保不影響 main isolate,但同時又能透過套件執行一些任務

實作範例

首先將 Root isolate 傳遞給 other isolates,讓 isolates 可以透過 Root isolate 進行初始化,代表兩者產生關聯,接著就可以存取套件了,例如:取得 flutter_secure_storage 資料、發送 flutter_local_notifications 本地通知

一開始我們創建 RootIsolateToken 以及 ReceivePort 實體,RootIsolateToken 要給 isolates 進行通訊的初始化,而 ReceivePort 為了接收 isolates 返回的結果。使用 Isolate.spawn 創建 isolate,給予執行函式和關鍵參數

void main() {
// 將 RootIsolateToken 傳遞給 background isolate 使用
RootIsolateToken rootIsolateToken = RootIsolateToken.instance!;
ReceivePort receivePort = ReceivePort();

Isolate.spawn(_isolateGetData, [rootIsolateToken, receivePort.sendPort]);
}

在 isolate 接收兩個參數,並執行 ensureInitialized()初始化RootIsolateToken,接著就能使用套件做一些處理,例如要從 FirebaseFirestore 取資料也行。最終再將訊息、數據經由 SendPort 回傳給 main isolate 使用,完成隔離的背景操作

void _isolateGetData(List<Object> args) async {
final rootIsolateToken = args[0] as RootIsolateToken;
final sendPort = args[1] as SendPort;

// 使用 RootIsolateToken 註冊 background isolate
BackgroundIsolateBinaryMessenger
.ensureInitialized(rootIsolateToken);

// 接下來就可以存取套件了
SharedPreferences sharedPreferences =
await SharedPreferences.getInstance();

...

// 取得結果後再透過 SendPort 返回 Main isolate
// sendPort.send(result);
}

BackgroundIsolateBinaryMessenger.ensureInitialized() 是最重要的環節,讓 isolate 可以跟 Root isolate 產生連結,綁定生命週期,在 APP 和 Flutter Engine 銷毀時可以關閉 Platform Channel,避免其他 isolates 嘗試跟 Flutter engine 溝通,造成不必要的消耗

2. Isolate.run()

  • run() 為一個更簡單且強大的 Api,省略原本使用 isolate 的複雜代碼,可替代原有的 compute() 操作
  • 不需要 SendPort、ReceivePort,也不用維護 isolate 的創建、銷毀,甚至錯誤捕捉都已經處理好了,只需要一般情境的 try catch 即可
  • 除了可以處理非同步的函式外,也支援同步處理,回傳值一律都為 Future
  • 函式參數可以使用匿名函式,不需要特別使用全域和靜態
  • 最後使用 exit() 將在 background isolate 中處理的結果內存回傳給 main isolate (不需要因為複製而導致佔用記憶體),然後安全地銷毀 isolate
  • 適合情境:一次性的任務處理
void main() async {
try {
final jsonData = await Isolate.run(_readAndParseJson);
} catch (error, stackTrace) {
debugPrint("$error , $stackTrace");
}
}

注意:如果應用是有支援 Native 平台跟 Web,會建議使用 compute(),在所有環境才能正常運作。如果確定只支援 Native 平台,就可使用 run() 高級 API

Conclusion

現在有了 Flutter 3.7 和 Dart 2.19,我們可以在提升 App 性能表現之外更精準的使用套件、存取原生平台的功能,趕快在專案上適當地使用它們吧!

Reference

About

Contribution

如果覺得文章不錯的話可以贊助,讓我有更多動力和熱情分享學習紀錄和生活!請我喝一杯咖啡吧~

https://www.buymeacoffee.com/yiichenhi

希望有幫助到你/妳,歡迎追蹤我,方便瀏覽最新的文章~

--

--

Yii Chen
Flutter Formosa

Flutter Lover || Organizer FlutterTaipei || Writer, Speaker || wanna make Flutter strong in Taiwan. https://linktr.ee/yiichenhi