Worker Manager for Flutter

repository·master·Indexed 43 days ago

https://github.com/dsrenesanse/worker_manager

A Flutter library for managing CPU-intensive tasks using isolates. It provides a singleton WorkerManager to prevent UI freezes through isolate reuse, scheduling, and advanced communication patterns. Features include support for regular, gentle, and port-based tasks, as well as a Cancelable class for explicit lifecycle management and inter-isolate communication via SendPort.

Tokens
4K
Snippets
17
Records
19
Agent score
55%

What's inside Worker Manager

  1. Customize iOS launch screen assets

    master

    To change the launch screen image in your iOS application, you can use one of two methods:

    1. Direct File Replacement: Replace the existing image files located in the example/ios/Runner/Assets.xcassets/LaunchImage.imageset/ directory with your own assets.
    2. Xcode Interface:
      • Open the iOS workspace using open ios/Runner.xcworkspace.
      • In the Xcode Project Navigator, navigate to Runner/Assets.xcassets.
      • Drag and drop your desired images into the asset catalog.
    open ios/Runner.xcworkspace
  2. Manage task lifecycle and cleanup

    master

    To manage the lifecycle of your worker manager and its tasks:

    • Cancel a task: Call .cancel() on the Cancelable object returned by an execute method to stop a running task.
    • Dispose Worker Manager: Call await workerManager.dispose() to clean up the manager and its resources.
    // Cancel a task
    cancelable.cancel();
    
    // Dispose Worker Manager
    await workerManager.dispose();
  3. Create and execute a Task

    master

    To run code in an isolate, wrap your logic in a Task object.

    Requirements for the task function:

    • The function must be a global function.
    • The function must accept exactly one parameter (e.g., int, String, or a custom Bundle class).
    • If using a custom class as a parameter, ensure it is prepared to handle the data passed via the bundle property.

    Task Parameters:

    • function: The global function to execute.
    • bundle: (Optional) The single argument to pass to the function.
    • timeout: (Optional) A Duration after which the task should be considered timed out.

    Execution:

    Use workerManager.manageWork(task: task) to execute the task. This returns a Stream that emits the result of your function. You should listen to this stream to receive results and handle errors using .onError().

    // 1. Define a global function with one parameter
    int fib(int n) {
      if (n < 2) return n;
      return fib(n - 2) + fib(n - 1);
    }
    
    // 2. Create the Task
    final task = Task(function: fib, bundle: 40, timeout: Duration(days: 78));
    
    // 3. Execute via WorkerManager
    workerManager.manageWork(task: task).listen((result) {
      print('Result: $result');
    }).onError((error) {
      print('Error: $error');
    });
  4. Initialize WorkerManager

    master

    To avoid micro-freezes during widget building, it is recommended to initialize the WorkerManager before your application starts. Call WorkerManager().initManager() in your main() function.

    Note: WorkerManager is a singleton. Once initialized, you can access it anywhere in your application.

    void main() async {
      await WorkerManager().initManager();
      runApp(MyApp());
    }
  5. Perform gentle cancellation in a Worker

    master

    If a task is long-running and you want to stop it without immediately killing the entire Isolate, use cancelGentle().

    When cancelGentle() is called, a CancelRequest is sent to the worker. Inside the worker's message handler, this sets a canceled flag to true and throws a CanceledError. This mechanism is intended to be used with tasks that support cooperative cancellation (e.g., tasks using ExecuteGentle or ExecuteGentleWithPort patterns) to allow the task to stop its work and exit cleanly.

    // Requesting a graceful shutdown of the current task
    worker.cancelGentle();
  6. Configure isolate management with dynamicSpawn

    master

    When initializing the worker_manager, you can use the dynamicSpawn flag to change how isolates are managed.

    • If dynamicSpawn is set to true, new isolates are spawned only when necessary and are killed when they are no longer needed.
    • If false (default behavior), the library uses reusable isolates to improve efficiency.
  7. Understand Task types in Worker Manager

    master

    Worker Manager uses different task types to define how work is executed and how it handles cancellation or communication. When submitting work, you will interact with one of these four execution models:

    1. Regular Tasks (TaskRegular): Standard asynchronous execution. The execution function is a simple FutureOr<R> Function().
    2. Tasks with Ports (TaskWithPort): Used for inter-isolate communication. The execution function receives a SendPort, and you provide an onMessage handler to process data sent back from the worker.
    3. Gentle Tasks (TaskGentle): Designed for tasks that can be stopped gracefully. The execution function receives a bool Function() (a predicate) that returns true if the task should stop.
    4. Gentle Tasks with Ports (TaskGentleWithPort): Combines both models. The execution function receives both a SendPort for communication and a cancellation predicate for graceful stopping.
  8. Execute a CPU-intensive task

    master

    Use the execute method to run a CPU-intensive function in a separate isolate. This returns a Cancelable object that allows you to manage the task's lifecycle.

    Note: The function passed to execute must be a static method, a globally defined function, or a lambda to ensure it is accessible from other isolates.

    Cancelable<ResultType> cancelable = workerManager.execute<ResultType>(
      () async {
        // Your CPU-intensive function here
      },
      priority: WorkPriority.immediately,
    );
  9. Execute a task with gentle cancellation and inter-isolate communication

    master

    The executeGentleWithPort method combines both features: it provides a SendPort for sending messages and an isCanceled getter for checking cancellation status. This is the most flexible way to run complex, communicative, and cancellable tasks.

    Cancelable<ResultType> cancelable = workerManager.executeGentleWithPort<ResultType, MessageType>(
      (SendPort sendPort, IsCanceled isCanceled) async {
        while (!isCanceled()) {
          // Your CPU-intensive function here
          // Use sendPort.send(message) to communicate with the main isolate
          // Check isCanceled() periodically to decide whether to continue or break the loop
        }
      },
      onMessage: (MessageType message) {
        // Handle the received message in the main isolate
      },
      priority: WorkPriority.immediately,
    );
  10. Execute a task with gentle cancellation

    master

    If your task involves a loop or long-running process, use executeGentle to allow for graceful termination. Instead of immediately killing the isolate, executeGentle provides an isCanceled callback. You should call this callback periodically within your task to check if the task should stop and clean up resources.

    Cancelable<ResultType> cancelable = workerManager.executeGentle<ResultType>(
      (isCanceled) async {
        while (!isCanceled()) {
          // Your CPU-intensive function here
          // Check isCanceled() periodically to decide whether to continue or break the loop
        }
      },
      priority: WorkPriority.immediately,
    );
  11. Execute a task with inter-isolate communication

    master

    Use executeWithPort to enable communication between the worker isolate and the main isolate. The task receives a SendPort which it can use to send messages (such as progress updates) back to the main isolate via the onMessage callback.

    Cancelable<ResultType> cancelable = workerManager.executeWithPort<ResultType, MessageType>(
      (SendPort sendPort) async {
        // Your CPU-intensive function here
        // Use sendPort.send(message) to communicate with the main isolate
      },
      onMessage: (MessageType message) {
        // Handle the received message in the main isolate
      },
    );