后台播放能力已调通,介入部分细节优化

This commit is contained in:
2026-08-20 22:36:10 +08:00
parent f94cda4234
commit bb8e4caf48
2 changed files with 303 additions and 49 deletions
+114 -49
View File
@@ -1,7 +1,7 @@
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:media_kit/media_kit.dart';
//import 'package:audio_service/audio_service.dart' as audio_service;
import 'package:audio_service/audio_service.dart' as audio_service;
import 'package:permission_handler/permission_handler.dart';
import 'services/audio_service.dart';
@@ -9,6 +9,7 @@ import 'services/playback_service.dart';
import 'services/app_lifecycle_service.dart';
import 'services/webdav_service.dart';
import 'services/audio_player_handler.dart';
import 'services/app_lifecycle_manager.dart';
import 'pages/home_page.dart';
import 'pages/player_page.dart';
import 'pages/playlist_page.dart';
@@ -23,6 +24,9 @@ bool _appInitialized = false;
// ⭐ 全局缓存 Handler
AudioPlayerHandler? _audioHandler;
// ⭐ 全局缓存 LifecycleManager
final AppLifecycleManager lifecycleManager = AppLifecycleManager();
void main() async {
// ⭐ P0:防止重复初始化
if (_appInitialized) {
@@ -53,6 +57,10 @@ void main() async {
providers: [
ChangeNotifierProvider(create: (_) => AudioService()),
ChangeNotifierProvider(create: (_) => AppLifecycleService()),
// ⭐ 生命周期管理器
ChangeNotifierProvider<AppLifecycleManager>(
create: (_) => lifecycleManager,
),
Provider<AudioPlayerHandler>(create: (_) => _audioHandler!),
],
child: const QTPlayerApp(),
@@ -121,8 +129,14 @@ class QTPlayerApp extends StatefulWidget {
}
class _QTPlayerAppState extends State<QTPlayerApp> {
// ⭐ build 计数器(诊断用)
static int _buildCount = 0;
bool _isInitializing = false;
// ⭐ 保存生命周期监听器引用
void Function(AppLifecycleStatus)? _lifecycleListener;
// ============================================================
// 生命周期
// ============================================================
@@ -134,92 +148,143 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
WidgetsBinding.instance.addPostFrameCallback((_) {
debugPrint('⏱️ T2 首帧回调: ${stopwatch.elapsedMilliseconds}ms');
// ⭐ 1. 初始化生命周期管理器
lifecycleManager.init();
// ⭐ 2. 绑定生命周期到播放器
_bindLifecycleToPlayback();
// ⭐ 3. 后台初始化
_startBackgroundInitialization();
});
}
@override
void dispose() {
// ⭐ 移除生命周期监听器
if (_lifecycleListener != null) {
lifecycleManager.removeListenerFromManager(_lifecycleListener!);
}
super.dispose();
}
// ============================================================
// 后台初始化(不阻塞首帧)
// 生命周期 → 播放器绑定
// ============================================================
void _bindLifecycleToPlayback() {
if (_lifecycleListener != null) {
lifecycleManager.removeListenerFromManager(_lifecycleListener!);
}
_lifecycleListener = (status) {
final audioService = context.read<AudioService>();
switch (status) {
case AppLifecycleStatus.paused:
case AppLifecycleStatus.inactive:
debugPrint('🎵 App 进入后台,播放继续');
break;
case AppLifecycleStatus.resumed:
debugPrint('🎵 App 回到前台');
break;
case AppLifecycleStatus.backgroundIdle:
debugPrint('🎵 长后台唤醒,检查播放状态');
break;
default:
break;
}
};
lifecycleManager.addListenerToManager(_lifecycleListener!);
}
// ============================================================
// 后台初始化(精细时间切片)
// ============================================================
Future<void> _startBackgroundInitialization() async {
if (_isInitializing) return;
_isInitializing = true;
final tStart = DateTime.now();
debugPrint('⏱️ T3 后台初始化开始');
// 1. PlaybackService(轻量级)
// ----- 1. PlaybackService -----
final t1 = DateTime.now();
try {
PlaybackService().init();
debugPrint('⏱️ T3.1 PlaybackService 初始化完成');
debugPrint('⏱️ T3.1 PlaybackService 完成: ${_elapsedMs(t1)}ms');
} catch (e) {
debugPrint('❌ PlaybackService 初始化失败: $e');
debugPrint('❌ PlaybackService 失败: $e');
}
// 2. ⭐ AudioService 暂时禁用(等 MainActivity 改为 AudioServiceActivity 后启用)
// await _initAudioService();
// 3. WebDAV
await _initWebDAV();
// 4. 通知权限
await _requestPermissions();
debugPrint('⏱️ T4 全部初始化完成');
}
// ════════════════════════════════════════════════════════════
// 子初始化方法
// ════════════════════════════════════════════════════════════
// ⭐ 暂时禁用,等 MainActivity 改为 AudioServiceActivity 后启用
// Future<void> _initAudioService() async {
// try {
// await audio_service.AudioService.init(
// builder: () => _audioHandler!,
// config: const audio_service.AudioServiceConfig(
// androidNotificationChannelId: 'com.example.qt_player.music',
// androidNotificationChannelName: '清听音乐播放',
// androidNotificationOngoing: true,
// androidStopForegroundOnPause: true,
// ),
// );
// debugPrint('✅ AudioService 初始化完成');
// } catch (e) {
// debugPrint('❌ AudioService 初始化失败: $e');
// }
// }
Future<void> _initWebDAV() async {
// ----- 2. WebDAV(只加载凭证) -----
final t2 = DateTime.now();
try {
final hasCred = await WebDAVService.instance.loadCredentials();
debugPrint(' WebDAV 加载完成, 已连接: $hasCred');
debugPrint('⏱️ T3.2 WebDAV 凭证加载完成: ${_elapsedMs(t2)}ms, 有凭证: $hasCred');
} catch (e) {
debugPrint('❌ WebDAV 加载失败: $e');
debugPrint('❌ WebDAV 凭证加载失败: $e');
}
}
Future<void> _requestPermissions() async {
// ----- 3. ⭐ AudioService(后台播放核心) -----
await _initAudioService();
// ----- 4. 通知权限 -----
final t3 = DateTime.now();
try {
if (await Permission.notification.isDenied) {
final status = await Permission.notification.request();
debugPrint('📢 通知权限状态: $status');
await Permission.notification.request();
debugPrint('⏱️ T3.3 通知权限完成: ${_elapsedMs(t3)}ms');
} else {
debugPrint('⏱️ T3.3 通知权限已授予: ${_elapsedMs(t3)}ms');
}
} catch (e) {
debugPrint('❌ 权限申请失败: $e');
}
final tEnd = DateTime.now();
debugPrint('⏱️ T4 全部初始化完成: ${tEnd.difference(tStart).inMilliseconds}ms');
}
// ============================================================
// ════════════════════════════════════════════════════════════
// ⭐ AudioService 初始化(后台播放核心)
// ════════════════════════════════════════════════════════════
Future<void> _initAudioService() async {
try {
await audio_service.AudioService.init(
builder: () => _audioHandler!,
config: const audio_service.AudioServiceConfig(
androidNotificationChannelId: 'com.example.qt_player.music',
androidNotificationChannelName: '清听音乐播放',
androidNotificationOngoing: true,
androidStopForegroundOnPause: true,
),
);
debugPrint('✅ AudioService 初始化完成');
} catch (e) {
debugPrint('❌ AudioService 初始化失败: $e');
}
}
// ════════════════════════════════════════════════════════════
// 辅助方法
// ════════════════════════════════════════════════════════════
String _elapsedMs(DateTime start) {
return '${DateTime.now().difference(start).inMilliseconds}';
}
// ════════════════════════════════════════════════════════════
// Build
// ============================================================
// ════════════════════════════════════════════════════════════
@override
Widget build(BuildContext context) {
debugPrint('⏱️ T1 build: ${stopwatch.elapsedMilliseconds}ms');
_buildCount++;
debugPrint(
'🎨 [QTPlayerApp] build #$_buildCount ${stopwatch.elapsedMilliseconds}ms');
// 监听歌曲变化,更新通知栏
final audioService = context.watch<AudioService>();
+189
View File
@@ -0,0 +1,189 @@
// lib/services/app_lifecycle_manager.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:media_kit/media_kit.dart';
/// App 生命周期状态(扩展了 Flutter 原生状态)
enum AppLifecycleStatus {
/// 应用正在启动(初始化阶段)
launching,
/// 应用在前台,用户可交互
resumed,
/// 应用在前台但不可交互(如:来电、系统弹窗)
inactive,
/// 应用在后台
paused,
/// 应用即将被销毁
detached,
/// 应用已进入后台且播放停止(自定义,用于播放策略)
backgroundIdle,
}
/// App 生命周期管理器
///
/// 职责:
/// 1. 统一监听 Flutter 的 AppLifecycleState
/// 2. 管理播放器在前后台的行为
/// 3. 管理渲染调度器的运行状态
/// 4. 管理 WebDAV 的缓存策略
class AppLifecycleManager extends ChangeNotifier {
static final AppLifecycleManager _instance = AppLifecycleManager._internal();
factory AppLifecycleManager() => _instance;
AppLifecycleManager._internal();
// ---- 当前状态 ----
AppLifecycleStatus _status = AppLifecycleStatus.launching;
AppLifecycleStatus get status => _status;
// ---- 上次进入后台的时间 ----
DateTime? _backgroundedAt;
DateTime? get backgroundedAt => _backgroundedAt;
// ---- 是否在后台 ----
bool get isInBackground =>
_status == AppLifecycleStatus.paused ||
_status == AppLifecycleStatus.inactive ||
_status == AppLifecycleStatus.detached;
bool get isInForeground => _status == AppLifecycleStatus.resumed;
// ---- 后台时长 ----
Duration get backgroundDuration {
if (_backgroundedAt == null) return Duration.zero;
return DateTime.now().difference(_backgroundedAt!);
}
// ---- 是否处于"长后台"(超过 5 分钟) ----
bool get isLongBackground => backgroundDuration > const Duration(minutes: 5);
// ---- 监听器 ----
final List<void Function(AppLifecycleStatus)> _listeners = [];
// ---- Flutter 原生生命周期订阅 ----
late final WidgetsBindingObserver _observer;
bool _isObserving = false;
// ---- 初始化 ----
void init() {
if (_isObserving) return;
_isObserving = true;
_observer = _AppLifecycleObserver(this);
WidgetsBinding.instance.addObserver(_observer);
// 获取初始状态
_updateStatus(AppLifecycleStatus.resumed);
debugPrint('📱 AppLifecycleManager 已初始化');
}
// ---- 状态更新 ----
void _handleNativeState(AppLifecycleState state) {
final newStatus = _convertNativeState(state);
// 状态变化时更新
if (newStatus != _status) {
_updateStatus(newStatus);
}
// 记录进入后台的时间
if (newStatus == AppLifecycleStatus.paused ||
newStatus == AppLifecycleStatus.inactive) {
if (_backgroundedAt == null) {
_backgroundedAt = DateTime.now();
debugPrint('📱 App 进入后台: ${_backgroundedAt}');
}
}
// 回到前台时,如果是长后台,触发通知
if (newStatus == AppLifecycleStatus.resumed) {
if (_backgroundedAt != null) {
final duration = backgroundDuration;
debugPrint('📱 App 回到前台,后台时长: ${duration.inMinutes}分钟');
_backgroundedAt = null;
// 如果是长后台,通知监听器做重连等操作
if (duration > const Duration(minutes: 5)) {
_notifyListeners(AppLifecycleStatus.backgroundIdle);
}
}
}
}
void _updateStatus(AppLifecycleStatus newStatus) {
final oldStatus = _status;
_status = newStatus;
debugPrint('📱 生命周期状态变化: $oldStatus$newStatus');
notifyListeners();
_notifyListeners(newStatus);
}
AppLifecycleStatus _convertNativeState(AppLifecycleState state) {
switch (state) {
case AppLifecycleState.resumed:
return AppLifecycleStatus.resumed;
case AppLifecycleState.inactive:
return AppLifecycleStatus.inactive;
case AppLifecycleState.paused:
return AppLifecycleStatus.paused;
case AppLifecycleState.detached:
return AppLifecycleStatus.detached;
default:
return AppLifecycleStatus.paused;
}
}
// ---- 自定义监听器管理 ----
void addListenerToManager(void Function(AppLifecycleStatus) listener) {
_listeners.add(listener);
}
void removeListenerFromManager(void Function(AppLifecycleStatus) listener) {
_listeners.remove(listener);
}
void _notifyListeners(AppLifecycleStatus status) {
for (final listener in _listeners) {
try {
listener(status);
} catch (e) {
debugPrint('❌ 生命周期监听器执行失败: $e');
}
}
}
// ---- 检查是否需要重新连接 WebDAV ----
bool get shouldRecheckWebDAV {
if (!isInForeground) return false;
if (_backgroundedAt == null) return false;
return isLongBackground;
}
// ---- 释放 ----
void dispose() {
if (_isObserving) {
WidgetsBinding.instance.removeObserver(_observer);
_isObserving = false;
}
_listeners.clear();
super.dispose();
}
}
/// 内部观察者(绑定 WidgetsBinding
class _AppLifecycleObserver extends WidgetsBindingObserver {
final AppLifecycleManager _manager;
_AppLifecycleObserver(this._manager);
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
_manager._handleNativeState(state);
}
}