Flutter后台工作线程插件flt_worker_nullsafety的使用

Flutter后台工作线程插件flt_worker_nullsafety的使用

flt_worker_nullsafety 插件允许你在专用的隔离(isolate)中调度并执行用Dart编写的后台任务。在Android上,它利用了WorkManager API,在iOS 13.0+上则使用了BackgroundTasks API。

后台处理适用于时间较长的任务,如下载/上传离线数据、拟合机器学习模型等。你可以使用该插件来安排这些任务。当系统决定运行任务时,会启动并运行一个预注册的Dart Worker。

集成

pubspec.yaml 文件中添加依赖:

dependencies:
  flt_worker_nullsafety: ^0.1.0

一个Worker在单独的Flutter引擎实例中运行。如果在Worker中需要任何插件,则必须重新注册它们。以下是一个例子,展示了如何为后台隔离注册 path_provider 插件。

iOS

- (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
  [GeneratedPluginRegistrant registerWithRegistry:self];

  // 设置回调以向无头引擎实例注册所有插件
  FltWorkerPlugin.registerPlugins = ^(NSObject<FlutterPluginRegistry> *registry) {
    [FLTPathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPathProviderPlugin"]];
  };
  ...
}

Android

@Override
public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
  GeneratedPluginRegistrant.registerWith(flutterEngine);

  // 设置回调以向无头引擎实例注册所有插件
  FltWorkerPlugin.registerPluginsForWorkers = registry -> {
    io.flutter.plugins.pathprovider.PathProviderPlugin.registerWith(
        registry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin"));
    return null;
  };
}

需要注意的是,flt_worker_nullsafety 插件本身总是可用的,因此无需再次注册。

如果你正在开发iOS应用,所有任务标识符必须在提交任何 BGTaskRequest 之前进行注册。

Info.plist 文件中添加以下行:

<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
  <string>com.example.counter_task</string>
  <string>dev.example.task2</string>
  ...
</array>

使用

初始化与工作调度器

在可以调度后台任务之前,必须向插件注册Worker回调:

import 'package:flt_worker_nullsafety/flt_worker.dart';

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

// 请注意,回调必须是一个顶级或静态函数

worker 函数作为所有后台任务的调度器,你可以根据工作负载的数据调用不同的函数,并返回一个 Future,以便插件可以在工作完成时通知系统调度器。

Future<void> worker(WorkPayload payload) {
  if (payload.tags.contains('download')) {
    return _fetchData();
  } else if (...) {
    ...
  } else {
    return Future.value();
  }
}

/// 缓存数据用于离线使用
Future<void> _fetchData() async {
  // 获取数据并更新本地存储
}

调度工作

你可以使用 enqueueWorkIntent 函数来调度后台 WorkIntent,如下所示:

enqueueWorkIntent(WorkIntent(
  identifier: 'counter',
  initialDelay: Duration(seconds: 59),
  input: <String, dynamic>{
    'counter': counter,
  },
));

名称 WorkIntent 被选择是为了避免与 WorkManager API 中的术语 WorkRequest 发生冲突。

请参阅文档及示例应用程序,了解如何调度不同类型的后台工作。

高级API与低级API

后台处理策略和API在Android和iOS平台上有很大差异。flt_worker_nullsafety 插件提供了统一且简化的API,适用于一般任务,如上面的例子所示。

然而,为了充分利用每个平台的后台处理功能,你可能需要考虑使用低级的平台特定API。

例如,在Android设备上,你可以使用 WorkManager API 来调度周期性工作:

import 'package:flt_worker_nullsafety/android.dart';

Future<void> _startPolling() async {
  await cancelAllWorkByTag('tag'); // 取消之前的任务
  await enqueueWorkRequest(const PeriodicWorkRequest(
    repeatInterval: Duration(hours: 4),
    flexInterval: Duration(minutes: 5),
    tags: ['tag'],
    constraints: WorkConstraints(
      networkType: NetworkType.connected,
      storageNotLow: true,
    ),
    backoffCriteria: BackoffCriteria(
      policy: BackoffPolicy.linear,
      delay: Duration(minutes: 1),
    ),
  ));
}

或者在iOS 13.0+ 上使用 BackgroundTasks API:

import 'package:flt_worker_nullsafety/ios.dart';

void _increaseCounter(int counter) {
  submitTaskRequest(BGProcessingTaskRequest(
    'com.example.counter_task',
    earliestBeginDate: DateTime.now().add(Duration(seconds: 10)),
    requiresNetworkConnectivity: false,
    requiresExternalPower: true,
    input: <String, dynamic>{
      'counter': counter,
    },
  ));
}

限制

这是一个新库,需要注意的一些限制包括:

  • 它依赖于 BackgroundTasks 框架,这意味着它不适用于iOS 13.0之前的版本。
  • 对于Android平台,WorkManager 的高级功能(如链式工作)尚未支持。

示例代码

以下是完整的示例代码,展示了如何使用 flt_worker_nullsafety 插件来调度后台任务。

示例代码

import 'dart:io';

import 'package:flt_worker_nullsafety/flt_worker.dart';
import 'package:flutter/material.dart';

import 'background_tasks_btc_prices.dart';
import 'background_tasks_counter.dart';
import 'btc_prices.dart';
import 'counter.dart';
import 'work_manager_btc_prices.dart';
import 'work_manager_counter.dart';
import 'worker.dart';

/// 后台处理示例。
///
/// 强制启动iOS后台任务:
/// ```
/// e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.btc_prices_task"]
/// e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.counter_task"]
/// e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.task1"]
/// e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"dev.example.task2"]
/// ```
///
/// > 查看 [iOS文档](https://developer.apple.com/documentation/backgroundtasks/starting_and_terminating_tasks_during_development)
void main() {
  runApp(MyApp());
  initializeWorker(worker);
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) => MaterialApp(
    home: Scaffold(
      appBar: AppBar(
        title: const Text('Worker 示例'),
      ),
      body: Builder(
        builder: (context) => SingleChildScrollView(
          child: Container(
            padding: const EdgeInsets.symmetric(vertical: 48, horizontal: 32),
            child: DefaultTextStyle(
              textAlign: TextAlign.center,
              style: const TextStyle(),
              child: Column(
                mainAxisSize: MainAxisSize.min,
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  const Text('高级API示例',
                    style: TextStyle(
                      fontSize: 14,
                      color: Colors.black45,
                    ),
                  ),
                  const SizedBox(height: 16),
                  TextButton(
                    child: const Text('计数器'),
                    onPressed: () => Navigator.push(context, MaterialPageRoute(
                      builder: (_) => Counter(),
                    )),
                  ),
                  TextButton(
                    child: const Text('比特币价格轮询'),
                    onPressed: () => Navigator.push(context, MaterialPageRoute(
                      builder: (_) => BtcPrices(),
                    )),
                  ),
                  Padding(
                    padding: const EdgeInsets.only(top: 48, bottom: 16),
                    child: const Text('低级平台特定API示例',
                      style: TextStyle(
                        fontSize: 14,
                        color: Colors.black45,
                      ),
                    ),
                  ),
                  ...(Platform.isAndroid ? _workManagerExamples(context) : _bgTasksExamples(context)),
                ],
              ),
            ),
          ),
        ),
      ),
    ),
  );
}

List<Widget> _workManagerExamples(BuildContext context) => [
  TextButton(
    child: const Text('计数器\n(一次性任务请求)'),
    onPressed: () => Navigator.push(context, MaterialPageRoute(
      builder: (_) => WorkManagerCounter(),
    )),
  ),
  TextButton(
    child: const Text('比特币价格轮询\n(周期性任务请求)'),
    onPressed: () => Navigator.push(context, MaterialPageRoute(
      builder: (_) => WorkManagerBtcPrices(),
    )),
  ),
];

List<Widget> _bgTasksExamples(BuildContext context) => [
  TextButton(
    child: const Text('计数器\n(处理任务请求)'),
    onPressed: () {
      Navigator.push(context, MaterialPageRoute(
        builder: (_) => BackgroundTasksCounter(),
      ));
    },
  ),
  TextButton(
    child: const Text('比特币价格轮询\n(应用刷新任务请求)'),
    onPressed: () {
      Navigator.push(context, MaterialPageRoute(
        builder: (_) => BackgroundTasksBtcPrices(),
      ));
    },
  ),
];

更多关于Flutter后台工作线程插件flt_worker_nullsafety的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter后台工作线程插件flt_worker_nullsafety的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flt_worker 是一个用于在 Flutter 中创建和管理后台工作线程的插件,支持空安全(null safety)。它允许你在后台执行耗时的任务,而不会阻塞 UI 线程,从而提高应用的性能和响应速度。

安装

首先,在 pubspec.yaml 文件中添加 flt_worker 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  flt_worker: ^1.0.0

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

基本使用

1. 导入插件

import 'package:flt_worker/flt_worker.dart';

2. 创建并启动工作线程

void startWorker() async {
  // 创建一个工作线程
  final worker = Worker();

  // 启动工作线程并传递一个任务
  worker.run((message) {
    // 这里是在后台线程中执行的代码
    print('Message from main thread: $message');

    // 模拟耗时操作
    Future.delayed(Duration(seconds: 2), () {
      print('Task completed in background thread');
    });

    // 返回结果给主线程
    return 'Task completed';
  }).then((response) {
    // 这里是在主线程中接收到的结果
    print('Response from worker: $response');
  });

  // 发送消息给工作线程
  worker.send('Hello from main thread');
}

3. 停止工作线程

void stopWorker() {
  worker.dispose();
}

详细说明

  • Worker: Worker 类用于创建和管理一个后台工作线程。
  • run: run 方法用于启动工作线程,并传递一个回调函数。这个回调函数将在后台线程中执行。
  • send: send 方法用于从主线程向工作线程发送消息。
  • dispose: dispose 方法用于停止和释放工作线程的资源。

示例

以下是一个完整的示例,展示了如何使用 flt_worker 插件在后台执行耗时任务:

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

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

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: WorkerExample(),
    );
  }
}

class WorkerExample extends StatefulWidget {
  [@override](/user/override)
  _WorkerExampleState createState() => _WorkerExampleState();
}

class _WorkerExampleState extends State<WorkerExample> {
  late Worker worker;

  [@override](/user/override)
  void initState() {
    super.initState();
    worker = Worker();
  }

  void startWorker() async {
    worker.run((message) {
      print('Message from main thread: $message');

      // 模拟耗时操作
      Future.delayed(Duration(seconds: 2), () {
        print('Task completed in background thread');
      });

      return 'Task completed';
    }).then((response) {
      print('Response from worker: $response');
    });

    worker.send('Hello from main thread');
  }

  [@override](/user/override)
  void dispose() {
    worker.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Flutter Worker Example'),
      ),
      body: Center(
        child: ElevatedButton(
          onPressed: startWorker,
          child: Text('Start Worker'),
        ),
      ),
    );
  }
}
回到顶部