Flutter后台服务插件flutter_background_service的使用
Flutter后台服务插件flutter_background_service的使用
flutter_background_service
是一个用于在Flutter应用程序中执行后台任务的插件。它允许开发者在应用处于前台或后台时运行 Dart 代码,这对于需要持续运行的任务(如数据同步、位置跟踪等)非常有用。本文将详细介绍如何配置和使用该插件,并提供完整的示例代码。
目录
支持
如果您觉得这个插件对您有帮助,请考虑通过Buy Me A Coffee来支持作者。
Android配置
必要的Gradle版本
确保您的项目使用以下Gradle版本:
classpath 'com.android.tools.build:gradle:7.4.2'
ext.kotlin_version = '1.8.10'
distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-all.zip
前台服务配置 (适用于Android 14及以上)
如果您的应用目标是SDK 34并且使用了前台服务,则需要添加额外的配置以声明使用的前台服务类型。例如:
<manifest xmlns:android="http://schemas.android.com/apk/res/android" ...>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
<application ...>
<service
android:name="id.flutter.flutter_background_service.BackgroundService"
android:foregroundServiceType="location" />
</application>
</manifest>
自定义通知图标
为了更改通知图标,请在res/drawable
文件夹下添加名为ic_bg_service_small
的图标资源。
iOS配置
对于iOS平台,您可以选择启用background_fetch
能力(可选),以便iOS执行IosConfiguration.onBackground
回调。此外,在iOS 13及以上版本中,您还需要向Info.plist
文件中添加如下内容:
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>dev.flutter.background.refresh</string>
</array>
同时,在AppDelegate.swift
中添加:
import flutter_background_service_ios
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
SwiftFlutterBackgroundServicePlugin.taskIdentifier = "your.custom.task.identifier"
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
使用自定义通知
您可以创建自定义的通知来增强用户体验。这里我们使用flutter_local_notifications
插件作为例子:
Future<void> initializeService() async {
final service = FlutterBackgroundService();
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'my_foreground', // id
'MY FOREGROUND SERVICE', // title
description: 'This channel is used for important notifications.', // description
importance: Importance.low, // importance must be at low or higher level
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(channel);
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: true,
isForegroundMode: true,
notificationChannelId: 'my_foreground',
initialNotificationTitle: 'AWESOME SERVICE',
initialNotificationContent: 'Initializing',
foregroundServiceNotificationId: 888,
),
...
保持后台服务运行
为了让后台服务即使在应用关闭后也能继续运行,您可以在onStart
方法中设置定时器来定期发送心跳信号或其他操作。另外,请注意对于某些设备制造商(如小米的MIUI),可能需要用户手动禁用电池优化才能保证服务不被杀死。
Socket.IO示例
下面是一个简单的Socket.IO实现,它可以在后台监听服务器事件并作出响应:
void onStart(ServiceInstance service) async {
final socket = io.io("your-server-url", <String, dynamic>{
'transports': ['websocket'],
'autoConnect': true,
});
socket.onConnect((_) {
print('Connected. Socket ID: ${socket.id}');
});
socket.onDisconnect((_) {
print('Disconnected');
});
socket.on("event-name", (data) {
// 处理接收到的数据,比如推送通知
});
Timer.periodic(const Duration(seconds: 1), (timer) {
socket.emit("event-name", "your-message");
print("service is successfully running ${DateTime.now().second}");
});
}
完整示例代码
以下是基于上述内容整理的一个完整的示例应用代码:
import 'dart:async';
import 'dart:io';
import 'dart:ui';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:shared_preferences/shared_preferences.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await initializeService();
runApp(const MyApp());
}
Future<void> initializeService() async {
final service = FlutterBackgroundService();
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'my_foreground',
'MY FOREGROUND SERVICE',
description: 'This channel is used for important notifications.',
importance: Importance.low,
);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
if (Platform.isIOS || Platform.isAndroid) {
await flutterLocalNotificationsPlugin.initialize(
const InitializationSettings(
iOS: DarwinInitializationSettings(),
android: AndroidInitializationSettings('ic_bg_service_small'),
),
);
}
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await service.configure(
androidConfiguration: AndroidConfiguration(
onStart: onStart,
autoStart: true,
isForegroundMode: true,
notificationChannelId: 'my_foreground',
initialNotificationTitle: 'AWESOME SERVICE',
initialNotificationContent: 'Initializing',
foregroundServiceNotificationId: 888,
foregroundServiceTypes: [AndroidForegroundType.location],
),
iosConfiguration: IosConfiguration(
autoStart: true,
onForeground: onStart,
onBackground: onIosBackground,
),
);
}
@pragma('vm:entry-point')
Future<bool> onIosBackground(ServiceInstance service) async {
WidgetsFlutterBinding.ensureInitialized();
DartPluginRegistrant.ensureInitialized();
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.reload();
final log = preferences.getStringList('log') ?? <String>[];
log.add(DateTime.now().toIso8601String());
await preferences.setStringList('log', log);
return true;
}
@pragma('vm:entry-point')
void onStart(ServiceInstance service) async {
DartPluginRegistrant.ensureInitialized();
SharedPreferences preferences = await SharedPreferences.getInstance();
await preferences.setString("hello", "world");
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
if (service is AndroidServiceInstance) {
service.on('setAsForeground').listen((event) {
service.setAsForegroundService();
});
service.on('setAsBackground').listen((event) {
service.setAsBackgroundService();
});
}
service.on('stopService').listen((event) {
service.stopSelf();
});
Timer.periodic(const Duration(seconds: 1), (timer) async {
if (service is AndroidServiceInstance) {
if (await service.isForegroundService()) {
flutterLocalNotificationsPlugin.show(
888,
'COOL SERVICE',
'Awesome ${DateTime.now()}',
const NotificationDetails(
android: AndroidNotificationDetails(
'my_foreground',
'MY FOREGROUND SERVICE',
icon: 'ic_bg_service_small',
ongoing: true,
),
),
);
service.setForegroundNotificationInfo(
title: "My App Service",
content: "Updated at ${DateTime.now()}",
);
}
}
debugPrint('FLUTTER BACKGROUND SERVICE: ${DateTime.now()}');
final deviceInfo = DeviceInfoPlugin();
String? device;
if (Platform.isAndroid) {
final androidInfo = await deviceInfo.androidInfo;
device = androidInfo.model;
} else if (Platform.isIOS) {
final iosInfo = await deviceInfo.iosInfo;
device = iosInfo.model;
}
service.invoke(
'update',
{
"current_date": DateTime.now().toIso8601String(),
"device": device,
},
);
});
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String text = "Stop Service";
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Service App'),
),
body: Column(
children: [
StreamBuilder<Map<String, dynamic>?>(
stream: FlutterBackgroundService().on('update'),
builder: (context, snapshot) {
if (!snapshot.hasData) {
return const Center(
child: CircularProgressIndicator(),
);
}
final data = snapshot.data!;
String? device = data["device"];
DateTime? date = DateTime.tryParse(data["current_date"]);
return Column(
children: [
Text(device ?? 'Unknown'),
Text(date.toString()),
],
);
},
),
ElevatedButton(
child: const Text("Foreground Mode"),
onPressed: () => FlutterBackgroundService().invoke("setAsForeground"),
),
ElevatedButton(
child: const Text("Background Mode"),
onPressed: () => FlutterBackgroundService().invoke("setAsBackground"),
),
ElevatedButton(
child: Text(text),
onPressed: () async {
final service = FlutterBackgroundService();
var isRunning = await service.isRunning();
isRunning ? service.invoke("stopService") : service.startService();
setState(() {
text = isRunning ? 'Start Service' : 'Stop Service';
});
},
),
const Expanded(
child: LogView(),
),
],
),
),
);
}
}
class LogView extends StatefulWidget {
const LogView({Key? key}) : super(key: key);
@override
State<LogView> createState() => _LogViewState();
}
class _LogViewState extends State<LogView> {
late final Timer timer;
List<String> logs = [];
@override
void initState() {
super.initState();
timer = Timer.periodic(const Duration(seconds: 1), (timer) async {
final SharedPreferences sp = await SharedPreferences.getInstance();
await sp.reload();
logs = sp.getStringList('log') ?? [];
if (mounted) {
setState(() {});
}
});
}
@override
void dispose() {
timer.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: logs.length,
itemBuilder: (context, index) {
final log = logs.elementAt(index);
return Text(log);
},
);
}
}
此示例展示了如何配置和使用flutter_background_service
插件来实现前后台模式切换、与主程序通信以及展示日志等功能。希望这些信息能帮助您更好地理解和使用该插件。
更多关于Flutter后台服务插件flutter_background_service的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter后台服务插件flutter_background_service的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用flutter_background_service
插件来实现后台服务的示例代码。这个插件允许你在Flutter应用中运行后台任务,即使应用不在前台也能执行任务。
1. 添加依赖
首先,在你的pubspec.yaml
文件中添加flutter_background_service
依赖:
dependencies:
flutter:
sdk: flutter
flutter_background_service: ^0.9.0 # 请检查最新版本号
然后运行flutter pub get
来安装依赖。
2. 配置Android
在android/app/src/main/AndroidManifest.xml
中添加必要的权限和声明服务:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.yourapp">
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<application
... >
<service
android:name=".MyBackgroundService"
android:enabled="true"
android:exported="true"
android:foregroundServiceType="location" />
...
</application>
</manifest>
3. 创建后台服务类
在android/app/src/main/kotlin/com/example/yourapp/
(或java/com/example/yourapp/
如果你使用Java)目录下创建一个新的Kotlin/Java类,例如MyBackgroundService.kt
(或MyBackgroundService.java
):
Kotlin:
package com.example.yourapp
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.plugin.common.MethodChannel
import com.transistorsoft.flutter_background_geolocation.FlutterBackgroundService
class MyBackgroundService : FlutterBackgroundService() {
override fun onCreate() {
super.onCreate()
// Initialize FlutterEngine
val flutterEngine = FlutterEngine(this)
flutterEngine.dartExecutor.executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
)
// Setup MethodChannel
val channel = MethodChannel(flutterEngine.dartExecutor.binaryMessenger, "com.example.yourapp/background_service")
channel.setMethodCallHandler { call, result ->
if (call.method == "startTask") {
// Handle start task logic here
result.success("Task started")
} else {
result.notImplemented()
}
}
// Keep FlutterEngine alive
flutterEngineCache
.put("my_engine_id", flutterEngine)
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
// Handle command to start service
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? {
return null
}
override fun onDestroy() {
super.onDestroy()
// Cleanup resources if needed
}
override fun onTaskRemoved(rootIntent: Intent?) {
super.onTaskRemoved(rootIntent)
// Handle task removal if needed
}
}
Java:
package com.example.yourapp;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import androidx.core.app.NotificationCompat;
import io.flutter.embedding.engine.FlutterEngine;
import io.flutter.embedding.engine.dart.DartExecutor;
import io.flutter.plugin.common.MethodChannel;
import com.transistorsoft.flutter_background_geolocation.FlutterBackgroundService;
public class MyBackgroundService extends FlutterBackgroundService {
@Override
public void onCreate() {
super.onCreate();
// Initialize FlutterEngine
FlutterEngine flutterEngine = new FlutterEngine(this);
flutterEngine.getDartExecutor().executeDartEntrypoint(
DartExecutor.DartEntrypoint.createDefault()
);
// Setup MethodChannel
MethodChannel channel = new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "com.example.yourapp/background_service");
channel.setMethodCallHandler((call, result) -> {
if (call.method.equals("startTask")) {
// Handle start task logic here
result.success("Task started");
} else {
result.notImplemented();
}
});
// Keep FlutterEngine alive
flutterEngineCache.put("my_engine_id", flutterEngine);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// Handle command to start service
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onDestroy() {
super.onDestroy();
// Cleanup resources if needed
}
@Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
// Handle task removal if needed
}
}
4. 在Dart代码中启动后台服务
在你的Flutter Dart代码中,使用MethodChannel
与后台服务进行通信:
import 'package:flutter/material.dart';
import 'package:flutter_background_service/flutter_background_service.dart';
void main() {
runApp(MyApp());
// Initialize background service
FlutterBackgroundService.initialize(
androidNotificationChannel: AndroidNotificationChannel(
id: 'com.example.yourapp/channel',
name: 'Background Service Channel',
description: 'Channel for background service notifications',
importance: NotificationImportance.High,
playSound: true,
),
onBackgroundMessage: backgroundMessageHandler,
);
}
void backgroundMessageHandler(Map<String, dynamic> message) {
if (message['type'] == 'startTask') {
// Handle start task logic here
print('Background task started');
}
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Flutter Background Service Example'),
),
body: Center(
child: ElevatedButton(
onPressed: () async {
// Start background service
bool isServiceRunning = await FlutterBackgroundService.isServiceRunning();
if (!isServiceRunning) {
await FlutterBackgroundService.startService(
androidConfig: AndroidConfig(
autoStart: true,
foregroundService: true,
),
);
// Send message to background service
FlutterBackgroundService.sendData({
'type': 'startTask',
});
}
},
child: Text('Start Background Service'),
),
),
),
);
}
}
注意事项
- 权限处理:确保在运行时请求必要的权限,如
FOREGROUND_SERVICE
和WAKE_LOCK
。 - 服务生命周期:理解并处理服务的生命周期,确保在不需要时正确停止服务。
- 通知处理:在Android 8.0及以上版本中,后台服务需要显示前台通知。
这个示例展示了如何设置和使用flutter_background_service
插件来运行后台任务。根据你的具体需求,你可能需要调整代码逻辑和配置。