Flutter并发执行插件flutter_isolate的使用

发布于 1周前 作者 gougou168 来自 Flutter

Flutter并发执行插件flutter_isolate的使用

FlutterIsolate简介

一个Dart isolate大致相当于一个独立的执行线程。在Flutter环境中,创建(“spawning”)一个isolate允许代码在主线程之外执行,这对于运行昂贵/长时间的任务非常重要,因为这些任务可能会阻塞UI。

然而,在生成的isolate中运行的代码通常无法与Flutter插件交互。这是由于平台插件脚手架和主应用程序isolate之间的紧密集成导致的。

FlutterIsolate插件通过引入FlutterIsolate类解决了这个问题,该类是围绕创建isolate的平台API以及为在生成的isolate中运行的代码设置必要的绑定以与Flutter插件通信的封装。

FlutterIsolate API

方法 Android iOS 描述
FlutterIsolate.spawn(entryPoint, message) 创建一个新的FlutterIsolate
FlutterIsolate.pause() 暂停正在运行的isolate
FlutterIsolate.resume() 恢复暂停的isolate
FlutterIsolate.kill() 终止一个isolate
FlutterIsolate.killAll() 终止所有当前运行的isolate
FlutterIsolate.runningIsolates 返回所有当前运行的isolate的ID
flutterCompute(callback, message) 创建一个新的FlutterIsolate,运行回调并返回结果

使用方法

创建FlutterIsolate

要创建一个FlutterIsolate,调用spawn方法,并传递一个带有@pragma('vm:entry-point')装饰器的顶层或静态函数:

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 extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return ElevatedButton(
      child: Text('Run'),
      onPressed: () {
        FlutterIsolate.spawn(someFunction, "hello world");
      },
    );
  }
}

执行单个任务

如果你只想创建一个isolate来执行单个任务(如Flutter的compute方法),可以调用flutterCompute:

@pragma('vm:entry-point')
Future<int> expensiveWork(int arg) async {
  int result = 0;
  // 假设这里有很多计算
  for (var i = 0; i < 1000000000; i++) {
    result += i;
  }
  return result;
}

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

在其他isolate中创建isolate

isolate也可以从其他isolate中创建:

import 'package:flutter_isolate/flutter_isolate.dart';

@pragma('vm:entry-point')
void isolate2(String arg) {
  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");
  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());
}

注意事项

  • 入口点必须是一个顶层函数,并且需要加上@pragma('vm:entry-point')注解。
  • 类级别的方法将不会工作,并会抛出异常。
  • 忘记添加@pragma('vm:entry-point')注解会导致应用在发布模式下崩溃。
  • 由于FlutterIsolate是由特定于平台的’view’支持的,因此事件循环不会在没有更多’用户’工作时终止,并且需要显式地使用kill()终止FlutterIsolates。

示例项目

下面是一个完整的示例项目,展示了如何使用flutter_isolate插件来下载文件:

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_isolate/flutter_isolate.dart';
import 'package:path_provider/path_provider.dart';
import 'package:flutter_downloader/flutter_downloader.dart';

@pragma('vm:entry-point')
void isolate2(String arg) {
  getTemporaryDirectory().then((dir) async {
    print("isolate2 temporary directory: $dir");

    await FlutterDownloader.initialize(debug: true);
    FlutterDownloader.registerCallback(AppWidget.downloaderCallback);

    final taskId = await FlutterDownloader.enqueue(
        url:
            "https://raw.githubusercontent.com/rmawatson/flutter_isolate/master/README.md",
        savedDir: dir.path);
  });
  Timer.periodic(
      Duration(seconds: 1), (timer) => print("Timer Running From Isolate 2"));
}

@pragma('vm:entry-point')
void isolate1(String arg) async {
  await FlutterIsolate.spawn(isolate2, "hello2");

  getTemporaryDirectory().then((dir) {
    print("isolate1 temporary directory: $dir");
  });
  Timer.periodic(
      Duration(seconds: 1), (timer) => print("Timer Running From Isolate 1"));
}

@pragma('vm:entry-point')
void computeFunction(String arg) async {
  getTemporaryDirectory().then((dir) {
    print("Temporary directory in compute function : $dir with arg $arg");
  });
}

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await FlutterDownloader.initialize(debug: true);
  runApp(MyApp());
}

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

class _MyAppState extends State<MyApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        home: Scaffold(
            appBar: AppBar(
              title: const Text('Plugin example app'),
            ),
            body: AppWidget()));
  }
}

class AppWidget extends StatelessWidget {
  static void downloaderCallback(String id, int status, int progress) {
    print("progress: $progress");
  }

  Future<void> _run() async {
    print(
        "Temp directory in main isolate : ${(await getTemporaryDirectory()).path}");
    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();
    });
  }

  @override
  Widget build(BuildContext context) {
    return Column(crossAxisAlignment: CrossAxisAlignment.center, children: [
      ElevatedButton(
        child: Text('Spawn isolates'),
        onPressed: _run,
      ),
      ElevatedButton(
        child: Text('Check running isolates'),
        onPressed: () async {
          final isolates = await FlutterIsolate.runningIsolates;
          await showDialog(
              builder: (ctx) {
                return Center(
                    child: Container(
                        color: Colors.white,
                        padding: EdgeInsets.all(5),
                        child: Column(
                            children: isolates
                                    .map((i) => Text(i))
                                    .cast<Widget>()
                                    .toList() +
                                [
                                  ElevatedButton(
                                      child: Text("Close"),
                                      onPressed: () {
                                        Navigator.of(ctx).pop();
                                      })
                                ])));
              },
              context: context);
        },
      ),
      ElevatedButton(
        child: Text('Kill all running isolates'),
        onPressed: () async {
          await FlutterIsolate.killAll();
        },
      ),
      ElevatedButton(
        child: Text('Run in compute function'),
        onPressed: () async {
          await flutterCompute(computeFunction, "foo");
        },
      ),
    ]);
  }
}

这个示例展示了如何使用flutter_isolate插件来管理多个isolate,包括创建、暂停、恢复和终止isolate,以及检查当前运行的isolate。


更多关于Flutter并发执行插件flutter_isolate的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter并发执行插件flutter_isolate的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,flutter_isolate 是一个用于在 Flutter 中实现并发执行的插件,它利用 Dart 的 dart:isolate 库来创建独立的 Dart 执行环境,从而避免阻塞主 UI 线程。下面是一个使用 flutter_isolate 插件的简单示例代码,展示如何并发执行一些任务。

首先,确保在你的 pubspec.yaml 文件中添加 flutter_isolate 依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_isolate: ^x.y.z  # 请使用最新版本号

然后运行 flutter pub get 来安装依赖。

接下来是示例代码,展示如何使用 flutter_isolate 并发执行计算任务:

import 'package:flutter/material.dart';
import 'package:flutter_isolate/flutter_isolate.dart';
import 'dart:math';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Flutter Isolate Example'),
        ),
        body: Center(
          child: IsolateExample(),
        ),
      ),
    );
  }
}

class IsolateExample extends StatefulWidget {
  @override
  _IsolateExampleState createState() => _IsolateExampleState();
}

class _IsolateExampleState extends State<IsolateExample> {
  String _result = 'Calculating...';

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        Text('Result: $_result'),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _calculate,
          child: Text('Calculate'),
        ),
      ],
    );
  }

  void _calculate() async {
    setState(() {
      _result = 'Calculating...';
    });

    // 使用 flutter_isolate 执行并发任务
    final compute = Compute(entryPoint: _computeIntensiveTask);
    final result = await compute.call(input: 42);

    setState(() {
      _result = 'Result: $result';
    });
  }

  // 这是一个计算密集型任务的示例
  static int _computeIntensiveTask(int input) {
    // 模拟长时间计算
    int result = 0;
    for (int i = 0; i < 1000000; i++) {
      result += pow(input.toDouble(), 2).toInt();
    }
    return result;
  }
}

解释

  1. 依赖添加:在 pubspec.yaml 中添加 flutter_isolate 依赖。

  2. 主应用结构MyApp 定义了应用的根结构,包含一个 Scaffold 和一个居中的 IsolateExample 组件。

  3. IsolateExample 组件:这是一个有状态的组件,包含一个显示结果的文本和一个按钮。点击按钮时,调用 _calculate 方法。

  4. _calculate 方法

    • 首先更新状态,显示“Calculating…”。
    • 使用 Compute 类创建一个并发执行的环境,并指定 _computeIntensiveTask 作为要执行的任务。
    • 调用 compute.call 方法,并传入参数 42。这个方法会返回一个 Future,表示异步执行的结果。
    • 当结果返回时,更新状态以显示计算结果。
  5. _computeIntensiveTask 函数:这是一个计算密集型任务的示例,模拟一个长时间的计算过程。

注意事项

  • flutter_isolate 插件允许你在后台线程执行 Dart 代码,从而避免阻塞 UI 线程。这对于需要执行长时间计算或 I/O 操作的应用特别有用。
  • 请注意,由于 Dart 的 isolate 之间不能直接共享内存,因此需要通过消息传递机制来交换数据。

希望这个示例能够帮助你理解如何在 Flutter 中使用 flutter_isolate 插件进行并发执行。如果你有任何进一步的问题,欢迎继续提问!

回到顶部