我正在使用flutter创建的警报应用程序。我正在使用android_alarm_manager_plus,flutter_foreground_task,flutter_local_notifications作为实现警报核心功能的主要软件包。我基本上正在构建警报应用程序,以在特定时间触发选定的日子触发警报。
当应用在前景中还活着时,该应用程序可以通过显示通知并播放警报声来发射警报。但是,当我终止我的应用程序时,设置了警报;它将显示通知。但是没有播放警报。我是我的PubSpec.yaml包
输入图像描述在此处(单击此链接以查看图像)alarmcallback&
foregroundStartServiceCallback代码(顶级功能)
@pragma('vm:entry-point')
void startForegroundTaskCallback() {
FlutterForegroundTask.setTaskHandler(ForegroundServiceHandler());
}
@pragma('vm:entry-point')
void alarmCallback(int id, Map<String, dynamic> params) async {
debugPrint("The alarm callback is running !");
bool isRunningService = await FlutterForegroundTask.isRunningService;
debugPrint("The foreground service is running : $isRunningService");
if (!isRunningService) {
debugPrint("Init foreground task");
ForegroundTaskService.init();
} else {
debugPrint("restart foreground task");
await FlutterForegroundTask.restartService();
}
try {
var map = params['params'] as Map<String, dynamic>;
var alarmModel = Alarmsmodel.fromMap(map);
debugPrint(alarmModel.toString());
assert(alarmModel.audioPath != null,
" Cannot play audio because ,audio path is null !");
ForegroundTaskService.saveData("audioPath", alarmModel.audioPath);
ServiceRequestResult serviceResult =
await ForegroundTaskService.startService();
if (serviceResult is ServiceRequestSuccess) {
debugPrint("service result is success !");
} else if (serviceResult is ServiceRequestFailure) {
debugPrint("service result is faliure !");
}
var weekDay = DateTime.now().weekday;
// logic to reschedule alarm in repetition pattern.
// await LocalAlarmService.rescheduleAlarm(alarmModel, weekDay);
} catch (e) {
debugPrint("Error : $e");
throw Exception("Something went wrong, after triggering alarmCallback");
}
}
这是管理flutter的前景服务班的前景服务班
@pragma('vm:entry-point')
void startForegroundTaskCallback() {
FlutterForegroundTask.setTaskHandler(ForegroundServiceHandler());
}
@pragma('vm:entry-point')
void alarmCallback(int id, Map<String, dynamic> params) async {
debugPrint("The alarm callback is running !");
bool isRunningService = await FlutterForegroundTask.isRunningService;
debugPrint("The foreground service is running : $isRunningService");
if (!isRunningService) {
debugPrint("Init foreground task");
ForegroundTaskService.init();
} else {
debugPrint("restart foreground task");
await FlutterForegroundTask.restartService();
}
try {
var map = params['params'] as Map<String, dynamic>;
var alarmModel = Alarmsmodel.fromMap(map);
debugPrint(alarmModel.toString());
assert(alarmModel.audioPath != null,
" Cannot play audio because ,audio path is null !");
ForegroundTaskService.saveData("audioPath", alarmModel.audioPath);
ServiceRequestResult serviceResult =
await ForegroundTaskService.startService();
if (serviceResult is ServiceRequestSuccess) {
debugPrint("service result is success !");
} else if (serviceResult is ServiceRequestFailure) {
debugPrint("service result is faliure !");
}
var weekDay = DateTime.now().weekday;
// logic to reschedule alarm in repetition pattern.
// await LocalAlarmService.rescheduleAlarm(alarmModel, weekDay);
} catch (e) {
debugPrint("Error : $e");
throw Exception("Something went wrong, after triggering alarmCallback");
}
}
class ForegroundServiceHandler extends TaskHandler {
@override
Future<void> onStart(DateTime timestamp, TaskStarter starter) async {
debugPrint("Fetching audio path.");
String? audioPath =
await FlutterForegroundTask.getData<String>(key: 'audioPath');
debugPrint("The audio path is $audioPath");
if (audioPath != null && audioPath.isNotEmpty) {
debugPrint(
"The audio path is not null neither its empty . Audiopath is $audioPath");
AudioService.instance.playAudioInForeground(audioPath);
} else {
debugPrint(
"Error : audioPath is missing. (ForegroundServiceHandler)(onStart)");
}
}
@override
void onNotificationButtonPressed(String id) {
super.onNotificationButtonPressed(id);
debugPrint("onNotificationButtonPressed");
if (id == "stop_audio") {
AudioService.instance.stopAudio();
}
FlutterForegroundTask.stopService();
}
@override
void onNotificationPressed() {
super.onNotificationPressed();
debugPrint("onNotificationPressed");
}
@override
void onNotificationDismissed() {
super.onNotificationDismissed();
debugPrint("onNotificationDismissed");
FlutterForegroundTask.stopService();
}
@override
void onReceiveData(Object data) {
super.onReceiveData(data);
debugPrint("onReceiveData");
}
@override
void onRepeatEvent(DateTime timestamp) {}
@override
Future<void> onDestroy(DateTime timestamp) async {
debugPrint("onDestroy invoked. Stopping service");
}
}
这是我的代码audio服务
class AudioService {
AudioPlayer? _audioPlayer;
bool isAudioPlaying = false;
static final AudioService instance = AudioService._internal();
factory AudioService() {
return instance;
}
AudioService._internal() {
_audioPlayer = AudioPlayer();
_audioPlayer?.setReleaseMode(ReleaseMode.loop);
_audioPlayer?.onPlayerStateChanged.listen((PlayerState state) {
isAudioPlaying = (state == PlayerState.playing);
});
}
void disposeAudio() {
_audioPlayer?.dispose();
_audioPlayer = null;
}
void playAudio(String path) async {
if (_audioPlayer == null) {
debugPrint("Audio player instance is null !");
} else {
debugPrint("Audio Player instance is not null !");
}
_audioPlayer ??= AudioPlayer(); //ensure its initialized.
if (isAudioPlaying) {
await _audioPlayer?.stop();
}
debugPrint("Playing audio for audiopath : $path");
await _audioPlayer?.play(
AssetSource(path),
volume: 1,
);
}
void stopAudio() async {
_audioPlayer ??= AudioPlayer();
debugPrint("Stopping audio instance hashcode : ${_audioPlayer.hashCode}");
await _audioPlayer!.stop();
}
void playAudioInForeground(String path) async {
String localPath = await getLocalFilePath(path);
debugPrint("Setting audio source url");
debugPrint("playing deviceFilePath .");
await _audioPlayer?.play(DeviceFileSource(localPath), volume: 1);
}
Future<String> getLocalFilePath(String assetPath) async {
// Get the app's document directory.
final directory = await getApplicationDocumentsDirectory();
final filePath = '${directory.path}/$assetPath';
debugPrint("File path is $filePath");
final file = File(filePath);
bool isFilePresent = await file.exists();
debugPrint("Does file exists $isFilePresent");
if (!file.existsSync()) {
// load assets as bytes.
debugPrint("loading bytes from rootbundle");
ByteData data = await rootBundle.load("assets/$assetPath");
debugPrint("loading bytes from ByteData");
List<int> bytes = data.buffer.asUint8List();
debugPrint("The bytes length is ${bytes.length}");
await file.create(recursive: true);
await file.writeAsBytes(bytes);
debugPrint("written to file");
}
debugPrint("returning device file path - $filePath");
return filePath;
}
}
看起来,当应用程序终止时,您的警报通知正在工作,但是警报音频没有播放。主要问题可能与Android中的背景执行限制有关。以下是一些可能的原因和解决方案:
修改AlarmCallback,如果应用程序未运行,请明确重新启动前景服务:
@pragma('vm:entry-point')
void alarmCallback(int id, Map<String, dynamic> params) async {
debugPrint("The alarm callback is running !");
// Ensure the Foreground Task is Running
bool isRunningService = await FlutterForegroundTask.isRunningService;
if (!isRunningService) {
debugPrint("Starting foreground service...");
ForegroundTaskService.init();
await Future.delayed(Duration(seconds: 2)); // Small delay to ensure service starts
}
debugPrint("Restarting foreground service...");
await FlutterForegroundTask.restartService();
try {
var map = params['params'] as Map<String, dynamic>;
var alarmModel = Alarmsmodel.fromMap(map);
assert(alarmModel.audioPath != null, "Cannot play audio - Path is null!");
// Save audio path to ForegroundTask
ForegroundTaskService.saveData("audioPath", alarmModel.audioPath);
ServiceRequestResult serviceResult = await ForegroundTaskService.startService();
if (serviceResult is ServiceRequestSuccess) {
debugPrint("Foreground service started successfully!");
} else {
debugPrint("Failed to start foreground service.");
}
} catch (e) {
debugPrint("Error in alarmCallback: $e");
}
}
确保音频播放正确始于Onstart内部:
更改文件路径的处理方式,以确保正确加载警报音频文件。
class AudioService {
AudioPlayer? _audioPlayer;
static final AudioService instance = AudioService._internal();
factory AudioService() {
return instance;
}
AudioService._internal() {
_audioPlayer = AudioPlayer();
_audioPlayer?.setReleaseMode(ReleaseMode.loop);
}
Future<void> playAudioInForeground(String path) async {
String localPath = await getLocalFilePath(path);
debugPrint("Playing audio from local path: $localPath");
try {
await _audioPlayer?.setSource(DeviceFileSource(localPath));
await _audioPlayer?.setVolume(1.0);
await _audioPlayer?.play(DeviceFileSource(localPath));
} catch (e) {
debugPrint("Error playing audio: $e");
}
}
Future<String> getLocalFilePath(String assetPath) async {
final directory = await getApplicationDocumentsDirectory();
final filePath = '${directory.path}/$assetPath';
final file = File(filePath);
if (!file.existsSync()) {
ByteData data = await rootBundle.load("assets/$assetPath");
List<int> bytes = data.buffer.asUint8List();
await file.create(recursive: true);
await file.writeAsBytes(bytes);
}
return filePath;
}
void stopAudio() async {
await _audioPlayer?.stop();
}
}
AndroidManifest.xml in android/app/src/main/main/androidManifest.xml中的Addd前景服务权限,添加:
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
declare前景服务在androidmanifest.xml内部...,添加:
<service
android:name="com.example.alarm.ForegroundService"
android:foregroundServiceType="mediaPlayback"
android:permission="android.permission.FOREGROUND_SERVICE"/>
屏蔽用户将应用程序添加到主屏幕中:
import 'package:device_info_plus/device_info_plus.dart';
import 'package:battery_optimization/battery_optimization.dart';
void requestBatteryOptimizationPermission() async {
bool isIgnoring = await BatteryOptimization.isIgnoringBatteryOptimizations();
if (!isIgnoring) {
await BatteryOptimization.openBatteryOptimizationSettings();
}
}
call requestBatteryOptimizationpermission()应用程序启动
void main() {
WidgetsFlutterBinding.ensureInitialized();
requestBatteryOptimizationPermission();
runApp(MyApp());
}