패키지, 저장소 및 앱 카테고리 검색

패키지 상세v2.1.0

flutter_isolate

FlutterIsolate는 Flutter 플러그인과 함께 작동하는 Dart Isolate를 Flutter에서 시작할 수 있는 방법을 제공합니다.

AndroidiOS
30일 다운로드
1.8만
좋아요
482
Pub 점수
150

패키지 설치

flutter pub add flutter_isolate

패키지 개요

공개 정보를 바탕으로 생성한 선택 가이드입니다.

FlutterIsolate는 Flutter 애플리케이션 내에서 Dart의 고립체를 생성하고 관리할 수 있게 해주며, 메인 UI 스레드를 차단하지 않고 CPU 집약적인 작업을 병렬로 실행할 수 있습니다. 이 패키지는 특히 Flutter 플러그인과 원활하게 작동하도록 설계되어 있으며, 고립체 기반 작업이 플랫폼별 기능과 안전하게 상호작용할 수 있도록 보장합니다. 이 패키지는 Dart의 고립체 모델을 활용하면서도 기존 플러그인 생태계와의 호환성을 유지함으로써 Flutter 앱에서 백그라운드 처리를 간소화합니다.

사용 사례

  • 백그라운드에서 무거운 계산 실행
  • UI를 동결시키지 않고 대규모 데이터 세트 처리
  • 격리된 환경에서 플랫폼별 코드 실행
  • 집중 작업 중 앱 반응성 향상
  • 병렬 워크플로에서 기존 Flutter 플러그인과의 통합

주요 기능

  • Flutter 플러그인과의 원활한 통합
  • Android 및 iOS 플랫폼 지원
  • 이소레이트를 시작하기 위한 간단한 API
  • 메인 스레드와 아이솔레이트 스레드 간의 안전한 통신
  • 고립 실행을 위한 내장 오류 처리

적합한 대상

  • Flutter에서 백그라운드 처리가 필요한 개발자
  • 이미지 또는 오디오 처리와 같은 CPU 집약적인 작업을 수행하는 앱
  • 격리가 필요한 플러터 플러그인을 사용하는 팀
  • UI 성능 향상을 목표로 하는 프로젝트
  • 보안되고 격리된 실행 환경이 필요한 응용 프로그램

고려 사항

  • Dart의 Isolate 및 메시지 전달에 대한 이해 필요
  • Android 및 iOS 플랫폼에만 제한됨
  • isolated 오버헤드로 인해 메모리 사용량이 증가할 수 있습니다
  • async/await로 충분한 I/O 바인드 작업에는 적합하지 않습니다
  • 크로스-이소레이트 통신의 디버깅에서의 잠재적 복잡성

원본 README

연결된 소스 저장소의 README 원문 스냅샷입니다. 최신 내용은 저장소에서 확인하세요.

FlutterIsolate

A Dart isolate is roughly equivalent to a single, independent execution thread. In a Flutter context, creating ("spawning") an isolate allows code to execute outside the main thread, which is important for running expensive/long-running tasks that would otherwise block the UI.

However, code in a spawned isolate will generally not be able to interact with Flutter plugins. This is due to the tight integration between the platform plugin scaffolding and the main application isolate.

The FlutterIsolate plugin fixes this with the introduction of a FlutterIsolate class, which is a wrapper around the platform APIs for creating isolates and setting up the bindings necessary for code running in spawned isolates to communicate with Flutter plugins.

FlutterIsolate API
Android iOS Description
FlutterIsolate.spawn(entryPoint,message) :white_check_mark: :white_check_mark: spawns a new FlutterIsolate
FlutterIsolate.pause() :white_check_mark: :white_check_mark: pauses a running isolate
FlutterIsolate.resume() :white_check_mark: :white_check_mark: resumed a paused isolate
FlutterIsolate.kill() :white_check_mark: :white_check_mark: kills a an isolate
FlutterIsolate.killAll() :white_check_mark: :white_check_mark: kills all currently running isolates
FlutterIsolate.runningIsolates :white_check_mark: :white_check_mark: returns the IDs associated with all currently running isolates
flutterCompute(callback,message) :white_check_mark: :white_check_mark: spawns a new FlutterIsolate, runs callback and returns the returned value
Usage

To spawn a FlutterIsolate, call the spawn method with a top-level or static function that has been annotated with the @pragma('vm:entry-point') decorator:

import 'package:flutter_isolate/flutter_isolate.dart';

@pragma('vm:entry-point')
void someFunction(String arg) { 
  print("Running in an isolate with argument : $arg");
}
...

class SomeWidget() {
...

@override
  Widget build(BuildContext context) {
    return ElevatedButton(
            child: Text('Run'),
            onPressed: () {
                FlutterIsolate.spawn(someFunction, "hello world");
            },
    );
}

If you just want to spawn an isolate to perform a single task (like the Flutter compute method), call flutterCompute:

@pragma('vm:entry-point')
Future<int> expensiveWork(int arg) async {
  int result;
  // lots of calculations
  return result;
}

Future<int> doExpensiveWorkInBackground() async {
  return await flutterCompute(expensiveWork, arg);
}

Isolates can also be spawned from other isolates:

import 'package:flutter_startup/flutter_startup.dart';
import 'package:flutter_isolate/flutter_isolate.dart';

@pragma('vm:entry-point')
void isolate2(String arg) {
  FlutterStartup.startupReason.then((reason){
    print("Isolate2 $reason");
  });
  Timer.periodic(Duration(seconds:1),(timer)=>print("Timer Running From Isolate 2"));
}

@pragma('vm:entry-point')
void isolate1(String arg) async  {

  final isolate = await FlutterIsolate.spawn(isolate2, "hello2");

  FlutterStartup.startupReason.then((reason){
    print("Isolate1 $reason");
  });
  Timer.periodic(Duration(seconds:1),(timer)=>print("Timer Running From Isolate 1"));
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  final isolate = await FlutterIsolate.spawn(isolate1, "hello");
  Timer(Duration(seconds:5), (){print("Pausing Isolate 1");isolate.pause();});
  Timer(Duration(seconds:10),(){print("Resuming Isolate 1");isolate.resume();});
  Timer(Duration(seconds:20),(){print("Killing Isolate 1");isolate.kill();});

  runApp(MyApp());
}
...

See example/lib/main.dart for example usage with the flutter_downloader plugin.

It is important to note that the entrypoint must be a top-level function, decorated with the `@pragma('vm:entry-point') annotation:

@pragma('vm:entry-point')
void topLevelFunction(Map<String, dynamic> args) {
  // performs work in an isolate
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  @override
  void initState() {
    FlutterIsolate.spawn(topLevelFunction, {});
    super.initState();
  }
  Widget build(BuildContext context) {
    return Container();
  }
}

or a static method:

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  
  @pragma('vm:entry-point')
  static void topLevelFunction(Map<String, dynamic> args) {
    // performs work in an isolate
  }

  @override
  void initState() {
    FlutterIsolate.spawn(_MyAppState.staticMethod, {});
    super.initState();
  }
  Widget build(BuildContext context) {
    return Container();
  }
}

A class-level method will not work and will throw an Exception:

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {

  
  void classMethod(Map<String, dynamic> args) {
    // don't do this!
  }

  @override
  void initState() {
    
    FlutterIsolate.spawn(classMethod, {}); // this will throw NoSuchMethodError: The method 'toRawHandle' was called on null.
    super.initState();
  }
  Widget build(BuildContext context) {
    return Container();
  }
}

Failure to add the @pragma('vm:entry-point') annotation will cause the app to crash in release mode.

Notes

Due to a FlutterIsolate being backed by a platform specific 'view', the event loop will not terminate when there is no more 'user' work left to do and FlutterIsolates will require explict termination with kill().

Additionally this plugin has not been tested with a large range of plugins, only a small subset I have been using such as flutter_notification, flutter_blue and flutter_startup.

Communicating between isolates

To pass data between isolates, a ReceivePort should be created on your (parent) isolate with the corresponding SendPort sent via the spawn method:

@pragma('vm:entry-point')
void spawnIsolate(SendPort port) {
  port.send("Hello!");
}

void main() {
  var port = ReceivePort();
  port.listen((msg) {
    print("Received message from isolate $msg");
  });
  var isolate = await FlutterIsolate.spawn(spawnIsolate, port.sendPort);

}

Only primitives can be sent via a SendPort - see the SendPort documentation for further details.

Custom plugin registrant

See the example project for a sample implementation using a custom plugin registrant.

iOS

By default, flutter_isolate will register all plugins provided by Flutter's automatically generated GeneratedPluginRegistrant.m file.

If you want to register, e.g. some custom FlutterMethodChannels, you can define a custom registrant:

// Defines a custom plugin registrant, to be used specifically together with FlutterIsolatePlugin
@objc(IsolatePluginRegistrant) class IsolatePluginRegistrant: NSObject {
    @objc static func register(withRegistry registry: FlutterPluginRegistry) {
        // Register channels for Flutter Isolate
        registerMethodChannelABC(bm: registry.registrar(forPlugin: "net.myapp.myChannelABC").messenger())

        // Register default plugins
        GeneratedPluginRegistrant.register(with: registry)
    }
}

// In AppDelegate.swift
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        let controller = window.rootViewController as! FlutterViewController

        // Register custom channels for Flutter
        registerMethodChannelABC(bm: controller.binaryMessenger) // <-- the custom method channel

        // Point FlutterIsolatePlugin to use our previously defined custom registrant.
        // The string content must be equal to the plugin registrant class annotation 
        // value: @objc(IsolatePluginRegistrant)
        FlutterIsolatePlugin.isolatePluginRegistrantClassName = "IsolatePluginRegistrant" // <--

        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }
}
Android

Define a custom plugin registrant, to be used specifically together with FlutterIsolatePlugin:

public final class CustomPluginRegistrant {
  public static void registerWith(@NonNull FlutterEngine flutterEngine) {
    flutterEngine.getPlugins().add(... [your plugin goes here]);
  }
}

Create a MainApplication class that sets this custom isolate registrant:

public class MainApplication extends FlutterApplication {
  public MainApplication() {
    FlutterIsolatePlugin.setCustomIsolateRegistrant(CustomPluginRegistrant.class);
  }
}

카테고리

의존성

의존성 목록 1 개

소스 저장소

이 패키지를 사용하는 프로젝트