flutter 实现app进入后台之后持续活动
为了在 Flutter 应用进入后台后仍能保持活动状态,可以使用如下方法:
- 利用 Flutter 插件方式,通过
WidgetsBindingObserver
监听应用的生命周期变化。在应用进入后台之后,使用Isolate.spawn()
启动一个新的分离的 Dart 代码执行器,从而保持应用继续运行。
具体实现步骤如下:
- 使用
WidgetsBindingObserver
监听生命周期变化:
class MyAppState extends State<MyApp> with WidgetsBindingObserver {
// ...
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
super.didChangeAppLifecycleState(state);
if (state == AppLifecycleState.paused) {
startIsolate();
}
}
// ...
}
- 在
startIsolate()
方法中启动新的 Dart 代码执行器:
void startIsolate() {
IsolateNameServer.registerPortWithName(
_backgroundChannelName, SendPort.receivePort);
if (_isolate == null) {
Isolate.spawn(backgroundTask, SendPort.receivePort);
}
}
void backgroundTask(SendPort sendPort) async {
final timer = Timer.periodic(Duration(seconds: 1), (Timer t) {
print('background task running');
});
sendPort?.send(null); // dummy message
}
在上面的代码中,startIsolate()
方法启动了一个新的分离的 Dart 代码执行器,并在这个执行器中运行名为 backgroundTask()
的任务。该任务在 backgroundTask()
中简单地使用 Timer
定时输出一条日志信息。
需要注意的是,上面的代码所启动的分离代码执行器,与应用主线程之间没有共享数据的方式,但使用 IsolateNameServer
可以建立一个通道进行信息传输。即在 MyAppState
的 initState()
方法中,可以添加下面这段代码来互相连接:
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
IsolateNameServer.registerPortWithName(ui.window.onReportTimings,
'com.example.isolatesample.performance')
.then((_) => print('Registered port'));
IsolateNameServer.registerPortWithName(_backgroundChannelName,
IsolateNameServer.lookupPortByName(_backgroundChannelName))
.then((_) => print('Registered $_backgroundChannelName'));
}
- 使用
android_alarm_manager
插件并结合flutter_isolate
插件实现应用进入后台仍可保持活动状态。
android_alarm_manager
插件可以用于在 Android 平台上通过 AlarmManager
启动后台 Dart 切片的计划。而 flutter_isolate
插件可以协助管理与创建切片和在切片之间传递消息。
具体实现步骤如下:
- 在
pubspec.yaml
中添加android_alarm_manager
与flutter_isolate
依赖:
dependencies:
flutter:
sdk: flutter
android_alarm_manager: ^0.5.4+3
flutter_isolate: ^1.0.1
-
添加 Android 工程设置。
-
创建一个新的切片 Dart 代码文件,在该文件中编写具体任务逻辑。
class ExecutionMessage {
final int id;
ExecutionMessage(this.id);
}
void main() async {
final FlutterIsolate isolate = FlutterIsolate(getTask, name: 'BackgroundIsolate');
}
Future<void> getTask() async {
print('BackgroundIsolate: Starting task...');
final SendPort mainSendPort = IsolateNameServer.lookupPortByName(_isolateName)!;
final ExecutionMessage message = const ExecutionMessage(42);
Timer.periodic(Duration(seconds: 1), (_) {
mainSendPort.send(message);
print('BackgroundIsolate: Sent message $message');
});
}
在这个切片处理代码文件中,首先使用 FlutterIsolate
初始化一个新的切片,随后使用 IsolateNameServer
获取 sendPort
并发送消息,最后使用 Timer.periodic()
来模拟一段任务逻辑。
- 在
MyAppState
类中创建一个新的 Dart 代码切片,并使用android_alarm_manager
来设置一个定时器并启动任务。
import 'package:android_alarm_manager/android_alarm_manager.dart';
import 'package:flutter_isolate/flutter_isolate.dart';
class MyAppState extends State<MyApp> with WidgetsBindingObserver {
late final FlutterIsolate _isolate;
static const String _isolateName = 'isolate';
Future<void> _initializeAlarm() async {
await AndroidAlarmManager.initialize();
await AndroidAlarmManager.oneShot(Duration(seconds: 5), 0, executeTask,
allowWhileIdle: true, wakeup: true);
}
Future<void> executeTask() async {
final SendPort mainSendPort = IsolateNameServer.lookupPortByName(_isolateName)!;
final ExecutionMessage message = const ExecutionMessage(42);
Timer.periodic(Duration(seconds: 1), (_) {
mainSendPort.send(message);
print('BackgroundIsolate: Sent message $message');
});
}
//...
}
在这里,首先确保 android_alarm_manager
与 flutter_isolate
插件初始化,接着使用 AndroidAlarmManager.oneShot()
设置一个定时任务,并在 _executeTask()
方法中执行逻辑。
启动应用之后进入后台一段时间,应该可以在控制台中看到如下类似的日志信息:
I/flutter (22731): BackgroundIsolate: Sent message Instance of 'ExecutionMessage'