搜尋套件、儲存庫與 APP 分類

套件詳情v0.5.8

combine

一個 Flutter 套件,允許您在 Isolate 中使用 MethodChannels,並提供簡化的 Isolate 和線程池 API。

近 30 天下載
1370
喜歡數
34
Pub 分數
70

安裝套件

flutter pub add combine

套件介紹

根據套件公開資料產生的選型評估,僅供參考。

Combine 是一個 Flutter 套件,可簡化在 Isolates 中使用 MethodChannels 的操作,提供直觀的 API 來管理 isolates 和線程池。它能有效於主執行緒之外執行 CPU 負載密集型任務,提升應用效能與回應速度。該套件抽象複雜的 isolate 通訊模式,讓整合背景處理至 Flutter 應用變得更簡單。透過支援在 isolates 內部進行 method channel 互動,Combine 簡化異步作業,無需深入理解 Dart 底層併發機制。

適用情境

  • 在背景中執行重型計算
  • 處理檔案的非同步操作
  • 在長時間執行的任務期間提升UI回應性
  • 使用執行緒池管理並發任務
  • 使用 MethodChannels 簡化隔離通訊

主要特色

  • 易於使用的隔離區管理
  • 內建執行緒池抽象
  • 隔離中的簡化 MethodChannel 結合
  • 對 async/await 模式的支援
  • 輕量且依賴極少

適合對象

  • 需要背景處理的開發者
  • CPU密集型操作的應用程式
  • 尋求簡化並發處理的團隊
  • 需要可擴展任務執行的項目
  • 針對高效率的Flutter應用

使用建議

  • 需要理解隔離區和執行緒
  • 有限的平台特定功能
  • 不適合I/O密集型任務
  • 使用多個隔離區可能會增加記憶體用量
  • 最適合複雜且計算密集型的工作負載

原始 README

以下為關聯原始碼儲存庫的 README 原文快照,最新內容請造訪原始碼儲存庫。

Combine logo

License: MIT Pub

This plugin Combines Isolate, MethodChannel and Thread Pool.
In other words it provides a way to use flutter plugins in Isolate or just work with user friendly API for Isolates.

Learn more in this article!

How it works with Method Channels. Briefly

For the MethodChannel combine is using BackgroundIsolateBinaryMessenger.

In the case of the BinaryMessenger combine sending its messages to the UI Isolate and then sends them to the platform. As for now, BackgroundIsolateBinaryMessenger can't be used for the BinaryMessenger.

What's the difference between MethodChannel and BinaryMessenger?
BinaryMessenger is a Low-Level API for sending messages to the platform. For example, it's used by the AssetBundle or event by the MethodChannel in the main Isolate.

Features

  • Create an Isolate.
  • Communicate with Isolate without extra code.
  • Use Method Channels in Isolate.
  • Efficiently execute tasks in Isolates pool.

Index

Usage

Short usage example

Combine is used to create Isolates.

CombineInfo isolateInfo = await Combine().spawn((context) {
  print("Argument from main isolate: ${context.argument}");

  context.messenger.messages.listen((message) {
    print("Message from main isolate: $message");
    context.messenger.send("Hello from isolate!");
  });
}, argument: 42);

isolateInfo.messenger
  ..messages.listen((message) {
    print("Message from isolate: $message");
  })
  ..send("Hello from main isolate!");

// Will print:
// Argument from main isolate: 42
// Message from main isolate: Hello from main isolate!
// Message from isolate: Hello from isolate!

CombineWorker is a pool of Isolates that efficiently executes tasks in them.

In comparison to Fluter's compute method which creates an isolate each time it is called, Combine Worker creates a pool of isolates and efficiently reuses them.

final fibonacciNumber = await CombineWorker().executeWithArg(calculateFibonacci, 42);
print(fibonacciNumber); // 1655801441

int calculateFibonacci(int number) {
  if (number == 1 || number == 2) {
    return 1;
  } else {
    return calculateFibonacci(number - 1) + calculateFibonacci(number - 2);
  }
}

Combine

Create and maintain Isolate
Create

CombineIsolate is just a representation of Isolate so when you create a CombineIsolate, an Isolate will be created under the hood. On the web, however, everything will be executed on the main isolate.

To create a new CombineIsolate you just need to call Combine().spawn(entryPointFunction). entryPointFunction is a function which will be called in Isolate.

CombineInfo will be returned which holds CombineIsolate to control Isolate and IsolateMessenger to communicate with it.

CombineInfo combineInfo = await Combine().spawn((context) {
  print("Hello from Isolate!!!");
});
Kill

You can use CombineIsolate.kill method to kill CombineIsolate.

CombineIsolate combineIsolate;
combineIsolate.kill(); // Kill Isolate.
Communicate with Isolate
IsolateContext

Do you remember context argument in entryPointFunction? Let's take a closer look at it.

IsolateContext holds an argument, passed while you spawn Isolate, IsolateMessenger which is used to communicate with original Isolate and CombineIsolate which is represents current Isolate.

Pass arguments

You can just use variables from closure or provide argument by passing it to the spawn function.

final argumentFromClosure = "This is argument from main Isolate";
Combine().spawn(
  (context) {
    final argument = context.argument as String;
    print(argument); // Print: This is my argument
    print(argumentFromClosure); // Print: This is argument from main Isolate
  },
  argument: "This is my argument",
);

Arguments from closure will be copied to the Isolate. They may be mutable however mutable variable won't be synchronized so if you change it in main Isolate it won't be changed in Combine Isolate.

Chat with Isolate

To chat with Isolate you can use IsolateMessenger. It has messages getter with stream of messages from Isolate and send method which sends message to Isolate.

In the created isolate you can get IsolateMessenger from IsolateContext.messenger. Another IsolateMessenger can be found in CombineIsolate.

CombineInfo combineInfo = await Combine().spawn((context) {
  context.messenger
    ..messages.listen(
      (event) => print("Message from Main Isolate: $event"),
    )
    ..send("Hello from Combine Isolate!");
});

combineInfo.messenger.messages.listen(
  (event) {
    print("Message from Combine Isolate: $event");
    combineInfo.messenger.send("Hello from Main Isolate!");
  },
);

This code will give the following output:

Message from Combine Isolate: Hello from Combine Isolate!
Message from Main Isolate: Hello from Main Isolate!

Dealing with MethodChannels
Configuration

Everything is already configured to work with MethodChannels or BinaryMessengers so you can just use them!

Combine().spawn((context) async {
  final textFromTestAsset = await rootBundle.loadString("assets/test.txt");
  print("Text from test asset: $textFromTestAsset");
  // Print: Text from test asset: Asset is loaded!
});

Explanation:

  • the point it that rootBundle uses BinaryMessenger (low level MethodChannel)
  • let's assume that the file assets/test.txt exists and contains Asset is loaded! text

Combine Worker

Initialize worker

To initialize worker you may call CombineWorker().initialize() however it can be lazily initialized on the first execution so you omit calling this method.

Also this method has isolatesCount, tasksPerIsolate and initializer parameters. The second parameter is used to set maximum number of tasks that one isolate can perform asynchronously. About the initializer parameter you can read below.

Execute tasks

You can execute task with from zero to five positional arguments using execute, executeWithArg, executeWith2Args, executeWith3Args, executeWith4Args and executeWith5Args methods.

final helloWorld = await CombineWorker().execute(zeroArgsFunction);
final maksim = await CombineWorker().executeWithArg(oneArgFunction, "Maksim");
final helloArshak  = await CombineWorker().executeWith2Args(
  twoArgsFunction, 
  "Hello", "Arshak!"
);

String zeroArgsFunction() => "Hello, World!";
String oneArgFunction(String str) => str;
String twoArgsFunction(String a, String b) => "$a, $b";

Also you can execute task with any many count of positional and named arguments with executeWithApply method. It works like Function.apply. It takes a function to execute and a list of positional and map of named arguments.

final argumentsDescription = await CombineWorker().executeWithApply(
  describeArgs, ["Hello"], {#namedArg: "World"},
);

String describeArgs(String arg, {required String namedArg}) {
  return "Positional argument: $arg. Named argument: $namedArg";
}

This is very powerful method, however I suggest you to use execute, executeWithArg, executeWith2Args etc methods.

If some task will throw an exception, corresponding execute function will completes with this exception.

Create a new instance

If you want to have a few workers with different settings for the separate tasks, you can create a new worker instance with the CombineWorker.newInstance factory.

Initialize worker isolates

Sometimes you need to execute some code once worker isolate is created. For example, to initialize db connection, configure API client, etc. It can be done with the initializer function parameter for the CombineWorker.initialize() method.
initializer is a function that will be executed in each worker isolate during their creation.

Close Worker

CombineWorker().close() method is used to close the current worker.
CombineWorker is a singleton but under the hood it uses a worker manager instance which can be closed and recreated. It may be useful if you want to cancel all running and awaiting tasks (i. e. on user logout).

When worker is closed it completes all tasks with CombineWorkerClosedException.
If you want to wait for remaining tasks set waitForRemainingTasks parameter to true. In that case they won't be completed with exception.

You can call execute or initialize methods without awaiting for this future. In that case new worker manager will be created.

Limitations

Method Channel

As MethodChannels are working using BackgroundIsolateBinaryMessenger they have their limitations.

Binary Messenger

Everything will work fine while BinaryMessenger.send method is used by you or your plugin.

However if BinaryMessenger.handlePlatformMessage is used by you or your plugin you may notice that these methods are not working. This may happen if you didn't send any data to the platform from this Isolate.

Why? In short the reason is that plugin just sends all messages from known binary messenger in Main Isolate to the Combine Isolate. However binary messenger becomes known when you send anything to it. The good news is when you want to receive messages from messenger using BinaryMessenger.handlePlatformMessage method almost always firstly you send some data to it so it is very unlikely that you will face this problem.

Closure variables

Isolate entryPoint function for spawn method or task function for the execute methods may be a first-level, as well as a static or top-level.

Also, it may use closure variables but with some restrictions:

  • closure variable will be copied (as every variable passed to isolate) so it won't be synchronized across Isolates.
  • if you use at least one variable from closure all closure variables will be copied to the Isolate due to this issue. It can lead to high memory consumption or event exception because some variables may contain native resources.

Due to the above points, I highly recommend you avoid using closure variables until this issue is fixed.

所屬分類

原始碼儲存庫

使用此套件的專案