Flutter隔离执行插件easy_isolate的使用

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

Flutter隔离执行插件easy_isolate的使用

简介

Easy Isolate 是一个用于在Flutter应用中轻松创建和管理独立线程(Isolates)的插件。它提供了一种抽象的方法来处理多线程,使得开发者可以更容易地在不同的线程中执行任务。以下是该插件的一些主要功能:

  • Worker:负责启动新的线程,并允许主线程与新线程之间进行消息传递。
  • Parallel:提供了runmapforeach方法,可以在不同线程中执行函数、映射列表或遍历列表。

安装

pubspec.yaml文件中添加依赖:

dependencies:
  easy_isolate: ^latest_version

使用方法

Worker

创建Worker并初始化

要开始使用Worker,你需要实例化一个Worker对象,并调用它的init方法。这个方法接受两个必须的参数:mainHandlerisolateHandler,以及可选的initialMessagequeueModeerrorHandlerexitHandler

void main() async {
  final worker = Worker();
  await worker.init(mainHandler, isolateHandler, 
      initialMessage: 'firstMessage', queueMode: true);
}

void mainHandler(dynamic data, SendPort isolateSendPort) {
  // Handle messages from the isolate
}

void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
  // Handle messages from the main thread
}

发送消息

可以通过worker.sendMessage或者mainHandler中的SendPort发送消息给Isolate。

// Using sendMessage
worker.sendMessage('Hello from main');

// Using SendPort in mainHandler
void mainHandler(dynamic data, SendPort isolateSendPort) {
  isolateSendPort.send('Hello from main');
}

接收消息

isolateHandler中接收来自主线程的消息,并通过mainSendPort发送消息回主线程。

void isolateHandler(dynamic data, SendPort mainSendPort, SendErrorFunction onSendError) {
  print('Received: $data');
  mainSendPort.send('Hello from isolate');
}

错误和退出处理

可以设置errorHandlerexitHandler来处理错误和线程关闭事件。

void errorHandler(dynamic data) {
  print('Error occurred: $data');
}

void exitHandler(dynamic data) {
  print('Isolate is closing');
}

关闭Worker

调用worker.dispose()来关闭Worker,并清理资源。

await worker.dispose();

Parallel

Run

在不同的线程中执行一个函数。

Future<void> main() async {
  final result = await Parallel.run(isEven, entryValue: 1);
  print(result);
}

bool isEven({int? item}) {
  return item != null && item % 2 == 0;
}

Map

对列表中的每个元素应用一个函数,并返回一个新的列表。

Future<void> main() async {
  final result = await Parallel.map([1, 2, 3, 4], intToStringAdapter);
  print(result); // Should print all the values as a String
}

String intToStringAdapter(int i) {
  return i.toString();
}

Foreach

遍历列表中的每个元素,并执行一个函数。

Future<void> main() async {
  await Parallel.foreach(['test'], writeFile);
}

void writeFile(String name) {
  File(Directory.systemTemp.path + '/$name').createSync();
}

示例代码

以下是一个完整的示例,展示了如何在一个模拟文件下载器中使用easy_isolate插件。

import 'dart:async';
import 'dart:isolate';
import 'dart:math';

import 'package:easy_isolate/easy_isolate.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: FileDownloaderScreen(),
    );
  }
}

class FileDownloaderScreen extends StatefulWidget {
  FileDownloaderScreen({Key? key}) : super(key: key);

  @override
  _FileDownloaderScreenState createState() => _FileDownloaderScreenState();
}

class _FileDownloaderScreenState extends State<FileDownloaderScreen> {
  final List<DownloadItem> items =
      List.generate(100, (index) => DownloadItem('Item ${index + 1}'));

  final Map<DownloadItem, double> itemsDownloadProgress = {};

  void notifyProgress(DownloadItemProgressEvent event) {
    final item = event.item;
    if (itemsDownloadProgress.containsKey(item)) {
      itemsDownloadProgress.update(
        item,
        (value) => event.progress,
      );
    } else {
      itemsDownloadProgress.addAll({item: event.progress});
    }
    setState(() {});
  }

  bool hasOngoingDownload(DownloadItem item) {
    return itemsDownloadProgress.containsKey(item);
  }

  @override
  Widget build(BuildContext context) {
    final theme = Theme.of(context);
    return Scaffold(
      body: Column(
        children: [
          SizedBox(height: 20),
          Text('File downloader simulation', style: theme.textTheme.headline5),
          SizedBox(height: 20),
          Expanded(
            child: ListView.builder(
              itemCount: items.length,
              itemBuilder: (context, index) {
                final item = items[index];
                return Card(
                  child: Padding(
                    padding: EdgeInsets.all(8),
                    child: Row(
                      children: [
                        Text(item.name),
                        Spacer(),
                        Text('${item.size}mb'),
                        SizedBox(width: 10),
                        if (hasOngoingDownload(item))
                          SizedBox(
                            height: 20,
                            width: 20,
                            child: CircularProgressIndicator(
                              value: itemsDownloadProgress[item],
                            ),
                          )
                        else ...[
                          IconButton(
                            icon: Icon(Icons.download),
                            onPressed: () => FileDownloaderWorker(
                              item: item,
                              onNotifyProgress: notifyProgress,
                            ).init(),
                          ),
                        ]
                      ],
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

class DownloadItem {
  DownloadItem(this.name) : size = Random().nextInt(50);

  final String name;
  final int size;

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      (other is DownloadItem &&
          runtimeType == other.runtimeType &&
          name == other.name &&
          size == other.size);

  @override
  int get hashCode => name.hashCode ^ size.hashCode;
}

class FileDownloaderWorker {
  FileDownloaderWorker({required this.onNotifyProgress, required this.item});

  final Function(DownloadItemProgressEvent event) onNotifyProgress;
  final DownloadItem item;
  final worker = Worker();

  Future<void> init() async {
    await worker.init(
      mainMessageHandler,
      isolateMessageHandler,
      errorHandler: print,
    );
    worker.sendMessage(DownloadItemEvent(item));
  }

  void mainMessageHandler(dynamic data, SendPort isolateSendPort) {
    if (data is DownloadItemProgressEvent) {
      onNotifyProgress(data);
    }
  }

  static isolateMessageHandler(
      dynamic data, SendPort mainSendPort, SendErrorFunction sendError) async {
    if (data is DownloadItemEvent) {
      final fragmentTime = 1 / data.item.size;
      double progress = 0;
      Timer.periodic(
        Duration(seconds: 1),
        (timer) {
          if (progress < 1) {
            progress += fragmentTime;
            mainSendPort.send(DownloadItemProgressEvent(data.item, progress));
          } else {
            timer.cancel();
          }
        },
      );
    }
  }
}

class DownloadItemEvent {
  DownloadItemEvent(this.item);

  final DownloadItem item;
}

class DownloadItemProgressEvent {
  DownloadItemProgressEvent(this.item, this.progress);

  final DownloadItem item;
  final double progress;
}

通过以上代码,你可以在Flutter应用中实现一个多线程的文件下载模拟器,每个下载任务都在单独的线程中运行,确保UI保持流畅。


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

1 回复

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


在Flutter中,easy_isolate 是一个用于在隔离环境中执行代码的插件,可以帮助开发者在 Flutter 应用中实现更安全和高效的并发执行。下面是一个使用 easy_isolate 插件的示例代码案例,展示了如何在 Flutter 应用中隔离执行一些代码。

首先,确保你已经在 pubspec.yaml 文件中添加了 easy_isolate 依赖:

dependencies:
  flutter:
    sdk: flutter
  easy_isolate: ^latest_version  # 请替换为实际的最新版本号

然后,运行 flutter pub get 来获取依赖。

接下来,在你的 Flutter 应用中使用 easy_isolate。以下是一个完整的示例,展示了如何在隔离环境中执行一个简单的计算任务:

import 'package:flutter/material.dart';
import 'package:easy_isolate/easy_isolate.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Easy Isolate Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  String result = '';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Easy Isolate Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'Result: $result',
              style: TextStyle(fontSize: 24),
            ),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () async {
                setState(() {
                  result = 'Calculating...';
                });

                // 使用 easy_isolate 执行隔离任务
                final isolateResult = await EasyIsolate.execute<int, String>(
                  entryPoint: calculateSum,
                  args: 1000,
                );

                setState(() {
                  result = 'Sum: $isolateResult';
                });
              },
              child: Text('Calculate Sum'),
            ),
          ],
        ),
      ),
    );
  }

  // 隔离执行的入口点函数
  static Future<int> calculateSum(int number) async {
    // 模拟一个耗时的计算任务
    await Future.delayed(Duration(seconds: 2));
    int sum = 0;
    for (int i = 1; i <= number; i++) {
      sum += i;
    }
    return sum;
  }
}

在这个示例中,我们创建了一个简单的 Flutter 应用,其中包含一个按钮和一个显示结果的文本。当用户点击按钮时,EasyIsolate.execute 方法被调用,以隔离的方式执行 calculateSum 函数。calculateSum 函数模拟了一个耗时的计算任务,并返回计算结果。

注意,EasyIsolate.execute 方法是异步的,并返回一个 Future。因此,我们在按钮点击事件中使用了 asyncawait 关键字来处理异步操作,并在计算完成后更新 UI。

确保在实际项目中根据需求调整代码,并处理可能的错误情况。

回到顶部