Flutter后台任务管理插件workmanager的使用

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

Flutter后台任务管理插件workmanager的使用

简介

Flutter WorkManager 是一个用于在 Flutter 应用中执行后台任务的插件。它封装了 Android 的 WorkManager 和 iOS 的 performFetchWithCompletionHandler 以及 BGAppRefreshTask,允许在应用不在前台运行时执行 Dart 代码。

对于 iOS 用户,建议观看苹果官方提供的关于背景处理的介绍视频:WWDC2020 - Session 10063。视频中讨论的所有约束同样适用于此插件。

特点

  • 支持一次性任务和周期性任务。
  • 可以设置任务的初始延迟、约束条件(如网络类型、电量状态等)。
  • 支持任务取消和重试机制。
  • 提供详细的调试支持。

平台设置

为了正确调度后台任务,必须先完成 Android 和 iOS 的平台设置:

使用方法

初始化插件

在注册任何任务之前,必须初始化 WorkManager 插件:

import 'package:workmanager/workmanager.dart';

@pragma('vm:entry-point') // 必须添加,特别是在应用混淆或使用 Flutter 3.1+ 时
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    print("Native called background task: $task");
    return Future.value(true);
  });
}

void main() {
  Workmanager().initialize(
    callbackDispatcher, // 回调函数
    isInDebugMode: true // 如果启用,会在任务运行时发布通知,便于调试
  );
  runApp(MyApp());
}

注册任务

一次性任务

注册一次性任务,任务只会执行一次:

Workmanager().registerOneOffTask(
  "task-identifier", 
  "simpleTask",
  initialDelay: Duration(minutes: 30), // 初始延迟时间
  constraints: Constraints(
    networkType: NetworkType.connected, // 需要网络连接
    requiresCharging: true, // 需要充电
  ),
  inputData: {'key': 'value'} // 输入数据
);

周期性任务

注册周期性任务,任务会定期执行:

Workmanager().registerPeriodicTask(
  "periodic-task-identifier", 
  "simplePeriodicTask", 
  frequency: Duration(hours: 1), // 执行频率
);

示例代码

以下是一个完整的示例代码,展示了如何使用 workmanager 插件来注册和管理后台任务:

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

import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:workmanager/workmanager.dart';

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

const simpleTaskKey = "be.tramckrijte.workmanagerExample.simpleTask";
const rescheduledTaskKey = "be.tramckrijte.workmanagerExample.rescheduledTask";
const failedTaskKey = "be.tramckrijte.workmanagerExample.failedTask";
const simpleDelayedTask = "be.tramckrijte.workmanagerExample.simpleDelayedTask";
const simplePeriodicTask = "be.tramckrijte.workmanagerExample.simplePeriodicTask";
const simplePeriodic1HourTask = "be.tramckrijte.workmanagerExample.simplePeriodic1HourTask";

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    switch (task) {
      case simpleTaskKey:
        print("$simpleTaskKey was executed. inputData = $inputData");
        final prefs = await SharedPreferences.getInstance();
        prefs.setBool("test", true);
        print("Bool from prefs: ${prefs.getBool("test")}");
        break;
      case rescheduledTaskKey:
        final key = inputData!['key']!;
        final prefs = await SharedPreferences.getInstance();
        if (prefs.containsKey('unique-$key')) {
          print('has been running before, task is successful');
          return true;
        } else {
          await prefs.setBool('unique-$key', true);
          print('reschedule task');
          return false;
        }
      case failedTaskKey:
        print('failed task');
        return Future.error('failed');
      case simpleDelayedTask:
        print("$simpleDelayedTask was executed");
        break;
      case simplePeriodicTask:
        print("$simplePeriodicTask was executed");
        break;
      case simplePeriodic1HourTask:
        print("$simplePeriodic1HourTask was executed");
        break;
      case Workmanager.iOSBackgroundTask:
        print("The iOS background fetch was triggered");
        Directory? tempDir = await getTemporaryDirectory();
        String? tempPath = tempDir.path;
        print(
            "You can access other plugins in the background, for example Directory.getTemporaryDirectory(): $tempPath");
        break;
    }

    return Future.value(true);
  });
}

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: Text("Flutter WorkManager Example"),
        ),
        body: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(8.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: <Widget>[
                Text(
                  "Plugin initialization",
                  style: Theme.of(context).textTheme.headlineSmall,
                ),
                ElevatedButton(
                  child: Text("Start the Flutter background service"),
                  onPressed: () {
                    Workmanager().initialize(
                      callbackDispatcher,
                      isInDebugMode: true,
                    );
                  },
                ),
                SizedBox(height: 16),

                ElevatedButton(
                  child: Text("Register OneOff Task"),
                  onPressed: () {
                    Workmanager().registerOneOffTask(
                      simpleTaskKey,
                      simpleTaskKey,
                      inputData: <String, dynamic>{
                        'int': 1,
                        'bool': true,
                        'double': 1.0,
                        'string': 'string',
                        'array': [1, 2, 3],
                      },
                    );
                  },
                ),
                ElevatedButton(
                  child: Text("Register rescheduled Task"),
                  onPressed: () {
                    Workmanager().registerOneOffTask(
                      rescheduledTaskKey,
                      rescheduledTaskKey,
                      inputData: <String, dynamic>{
                        'key': Random().nextInt(64000),
                      },
                    );
                  },
                ),
                ElevatedButton(
                  child: Text("Register failed Task"),
                  onPressed: () {
                    Workmanager().registerOneOffTask(
                      failedTaskKey,
                      failedTaskKey,
                    );
                  },
                ),
                ElevatedButton(
                    child: Text("Register Delayed OneOff Task"),
                    onPressed: () {
                      Workmanager().registerOneOffTask(
                        simpleDelayedTask,
                        simpleDelayedTask,
                        initialDelay: Duration(seconds: 10),
                      );
                    }),
                SizedBox(height: 8),
                ElevatedButton(
                    child: Text("Register Periodic Task (Android)"),
                    onPressed: Platform.isAndroid
                        ? () {
                            Workmanager().registerPeriodicTask(
                              simplePeriodicTask,
                              simplePeriodicTask,
                              initialDelay: Duration(seconds: 10),
                            );
                          }
                        : null),
                ElevatedButton(
                    child: Text("Register 1 hour Periodic Task (Android)"),
                    onPressed: Platform.isAndroid
                        ? () {
                            Workmanager().registerPeriodicTask(
                              simplePeriodicTask,
                              simplePeriodic1HourTask,
                              frequency: Duration(hours: 1),
                            );
                          }
                        : null),
                SizedBox(height: 16),
                Text(
                  "Task cancellation",
                  style: Theme.of(context).textTheme.headlineSmall,
                ),
                ElevatedButton(
                  child: Text("Cancel All"),
                  onPressed: () async {
                    await Workmanager().cancelAll();
                    print('Cancel all tasks completed');
                  },
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

调试提示

为了更好地调试后台任务,可以在 executeTask 中使用 try-catch 捕获异常,并记录日志:

@pragma('vm:entry-point')
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) async {
    try {
      // 任务逻辑
    } catch (e) {
      print("Error in background task: $e");
      throw Exception(e);
    }
    return Future.value(true);
  });
}

其他功能

  • 标签(Tagging):可以为任务设置标签,方便批量取消任务。
  • 现有任务策略(Existing Work Policy):指定当相同任务被多次调度时的行为,默认为 KEEP
  • 约束条件(Constraints):可以设置任务执行的条件,如网络类型、电量状态等。
  • 输入数据(InputData):可以为任务传递一些输入数据。
  • 重试策略(BackoffPolicy):指定任务失败后的重试策略,默认为指数退避。

通过以上内容,您可以轻松地在 Flutter 应用中管理和调度后台任务。希望这些信息对您有所帮助!


更多关于Flutter后台任务管理插件workmanager的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter后台任务管理插件workmanager的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,以下是一个关于如何在Flutter项目中使用workmanager插件进行后台任务管理的示例代码。workmanager是一个强大的插件,它允许你在Flutter应用中调度和执行后台任务,即使在应用被关闭或设备重启后也能继续运行。

1. 添加依赖

首先,你需要在pubspec.yaml文件中添加workmanager的依赖:

dependencies:
  flutter:
    sdk: flutter
  workmanager: ^0.7.0  # 请检查最新版本号

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

2. 配置Android和iOS

Android

android/app/src/main/AndroidManifest.xml中添加必要的权限:

<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

android/app/src/main/kotlin/[your_package_name]/MainActivity.kt(或对应的Java文件)中,初始化WorkManager:

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import io.flutter.embedding.android.FlutterActivity
import androidx.work.Configuration
import androidx.work.WorkManager

class MainActivity: FlutterActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Initialize WorkManager
        val configuration = Configuration.Builder()
            .setMinimumLoggingLevel(androidx.work.Logger.LEVEL_INFO)
            .build()
        WorkManager.initialize(this, configuration)
    }
}

iOS

ios/Runner/Info.plist中添加必要的后台模式:

<key>UIBackgroundModes</key>
<array>
    <string>fetch</string>
    <string>processing</string>
    <string>remote-notification</string>
</array>

3. 使用WorkManager

在你的Flutter项目中,你可以通过以下方式使用workmanager插件来调度和执行后台任务。

初始化WorkManager

在你的Dart文件中,首先导入workmanager包:

import 'package:workmanager/workmanager.dart';

然后,在应用的入口点(通常是main.dart)初始化WorkManager:

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  await Workmanager.initialize(
    callbackDispatcher, // 回调分发器
    handleIntent // 处理intent的回调
  );
  runApp(MyApp());
}

定义任务

定义一个唯一的任务标识符和任务内容:

const simpleTaskKey = "com.example.flutterapp/simpleTask";

void registerTasks() {
  Workmanager.registerOneOffTask(
    simpleTaskKey,
    "Simple Task Description",
    inputData: <String, dynamic>{
      'taskDataKey': 'taskDataValue',
    },
    constraints: Constraints(
      requiresBatteryNotLow: true,
      requiresCharging: false,
      requiresStorageNotLow: true,
      requiresDeviceIdle: false,
      requiresNetworkType: NetworkType.CONNECTED, // 需要网络连接
      deviceIdleMode: DeviceIdleMode.not_required,
      isRequiredInitialTask: false,
      priority: TaskPriority.high,
      storageNotLow: true,
      batteryNotLow: true,
    )
  );
}

回调处理

实现回调分发器和处理intent的回调:

void callbackDispatcher() {
  Workmanager.executeTask((task, inputData) async {
    // 这里处理任务逻辑
    print("Executing task: ${task.taskId}");
    if (task.taskId == simpleTaskKey) {
      // 处理输入数据
      print("Input data: ${inputData['taskDataKey']}");
      // 模拟任务执行
      await Future.delayed(Duration(seconds: 5));
      return Workmanager.resultSuccess();
    }
    return Workmanager.resultFailure();
  });
}

void handleIntent(Intent intent) {
  // 处理从后台任务返回的intent(如果需要)
}

启动任务

在应用中的适当位置调用registerTasks函数来注册并启动任务:

@override
void initState() {
  super.initState();
  registerTasks();
}

这个示例展示了如何在Flutter项目中使用workmanager插件来调度和执行后台任务。你可以根据自己的需求修改任务的内容和回调处理逻辑。

回到顶部