权限配置暂时到此,后台播放问题并入生命周期管理同步开发
This commit is contained in:
+142
-37
@@ -1,32 +1,59 @@
|
||||
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:permission_handler/permission_handler.dart';
|
||||
|
||||
import 'services/audio_service.dart';
|
||||
import 'services/playback_service.dart';
|
||||
import 'services/app_lifecycle_service.dart';
|
||||
import 'services/webdav_service.dart';
|
||||
import 'services/audio_player_handler.dart';
|
||||
import 'pages/home_page.dart';
|
||||
import 'pages/player_page.dart';
|
||||
import 'pages/playlist_page.dart';
|
||||
import 'widgets/global_mini_player.dart';
|
||||
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
// ⏱️ 全局计时器
|
||||
final stopwatch = Stopwatch();
|
||||
|
||||
void main() {
|
||||
// ⭐ 防止 Hot Restart / 热重载时重复初始化
|
||||
bool _appInitialized = false;
|
||||
|
||||
// ⭐ 全局缓存 Handler
|
||||
AudioPlayerHandler? _audioHandler;
|
||||
|
||||
void main() async {
|
||||
// ⭐ P0:防止重复初始化
|
||||
if (_appInitialized) {
|
||||
debugPrint('⏱️ main 已初始化,跳过重复执行');
|
||||
return;
|
||||
}
|
||||
_appInitialized = true;
|
||||
|
||||
stopwatch.start();
|
||||
debugPrint('⏱️ T0 main: ${stopwatch.elapsedMilliseconds}ms');
|
||||
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
debugPrint('⏱️ T0.5 ensureInitialized: ${stopwatch.elapsedMilliseconds}ms');
|
||||
|
||||
// ⭐ MediaKit 在 runApp 前初始化(官方要求)
|
||||
try {
|
||||
MediaKit.ensureInitialized();
|
||||
debugPrint('⏱️ T0.6 MediaKit 初始化完成');
|
||||
} catch (e) {
|
||||
debugPrint('❌ MediaKit 初始化失败: $e');
|
||||
}
|
||||
|
||||
// ⭐ 创建 Handler(AudioService 稍后初始化)
|
||||
_audioHandler = AudioPlayerHandler();
|
||||
|
||||
runApp(
|
||||
MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider(create: (_) => AudioService()),
|
||||
ChangeNotifierProvider(create: (_) => AppLifecycleService()),
|
||||
Provider<AudioPlayerHandler>(create: (_) => _audioHandler!),
|
||||
],
|
||||
child: const QTPlayerApp(),
|
||||
),
|
||||
@@ -35,13 +62,15 @@ void main() {
|
||||
debugPrint('⏱️ T0.8 runApp 完成: ${stopwatch.elapsedMilliseconds}ms');
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// 路由管理器(控制 MiniPlayer 显示/隐藏)
|
||||
// ════════════════════════════════════════════════════════════
|
||||
class RouteManager extends ChangeNotifier {
|
||||
static final RouteManager _instance = RouteManager._internal();
|
||||
factory RouteManager() => _instance;
|
||||
RouteManager._internal();
|
||||
|
||||
String _currentRoute = '/';
|
||||
|
||||
String get currentRoute => _currentRoute;
|
||||
|
||||
bool get showMiniPlayer =>
|
||||
@@ -56,6 +85,9 @@ class RouteManager extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// 导航观察者(监听路由变化)
|
||||
// ════════════════════════════════════════════════════════════
|
||||
class MiniPlayerNavigatorObserver extends NavigatorObserver {
|
||||
void _updateRoute(Route? route) {
|
||||
final name = route?.settings.name ?? '/';
|
||||
@@ -78,6 +110,9 @@ class MiniPlayerNavigatorObserver extends NavigatorObserver {
|
||||
}
|
||||
}
|
||||
|
||||
// ════════════════════════════════════════════════════════════
|
||||
// 主应用
|
||||
// ════════════════════════════════════════════════════════════
|
||||
class QTPlayerApp extends StatefulWidget {
|
||||
const QTPlayerApp({super.key});
|
||||
|
||||
@@ -86,21 +121,123 @@ class QTPlayerApp extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _QTPlayerAppState extends State<QTPlayerApp> {
|
||||
bool _isInitializing = false;
|
||||
|
||||
// ============================================================
|
||||
// 生命周期
|
||||
// ============================================================
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
debugPrint('⏱️ T1.5 initState: ${stopwatch.elapsedMilliseconds}ms');
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
debugPrint('⏱️ T2 首帧回调: ${stopwatch.elapsedMilliseconds}ms');
|
||||
_initializeServices();
|
||||
_startBackgroundInitialization();
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 后台初始化(不阻塞首帧)
|
||||
// ============================================================
|
||||
Future<void> _startBackgroundInitialization() async {
|
||||
if (_isInitializing) return;
|
||||
_isInitializing = true;
|
||||
|
||||
debugPrint('⏱️ T3 后台初始化开始');
|
||||
|
||||
// 1. PlaybackService(轻量级)
|
||||
try {
|
||||
PlaybackService().init();
|
||||
debugPrint('⏱️ T3.1 PlaybackService 初始化完成');
|
||||
} catch (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 {
|
||||
try {
|
||||
final hasCred = await WebDAVService.instance.loadCredentials();
|
||||
debugPrint('✅ WebDAV 加载完成, 已连接: $hasCred');
|
||||
} catch (e) {
|
||||
debugPrint('❌ WebDAV 加载失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _requestPermissions() async {
|
||||
try {
|
||||
if (await Permission.notification.isDenied) {
|
||||
final status = await Permission.notification.request();
|
||||
debugPrint('📢 通知权限状态: $status');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint('❌ 权限申请失败: $e');
|
||||
}
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// Build
|
||||
// ============================================================
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
debugPrint('⏱️ T1 build: ${stopwatch.elapsedMilliseconds}ms');
|
||||
|
||||
// 监听歌曲变化,更新通知栏
|
||||
final audioService = context.watch<AudioService>();
|
||||
final handler = context.read<AudioPlayerHandler>();
|
||||
final song = audioService.currentSong;
|
||||
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (song != null) {
|
||||
handler.updateNotification(
|
||||
id: song.id,
|
||||
title: song.title,
|
||||
artist: song.artist,
|
||||
);
|
||||
} else {
|
||||
handler.mediaItem.add(null);
|
||||
}
|
||||
});
|
||||
|
||||
return MaterialApp(
|
||||
title: '清听',
|
||||
theme: ThemeData(
|
||||
@@ -144,35 +281,3 @@ class _QTPlayerAppState extends State<QTPlayerApp> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void _initializeServices() async {
|
||||
debugPrint('⏱️ T3 开始初始化: ${stopwatch.elapsedMilliseconds}ms');
|
||||
final lifecycle = AppLifecycleService();
|
||||
|
||||
try {
|
||||
lifecycle.updateStatus(AppStatus.mediaKitInitializing);
|
||||
MediaKit.ensureInitialized();
|
||||
debugPrint('⏱️ T4 MediaKit 完成: ${stopwatch.elapsedMilliseconds}ms');
|
||||
lifecycle.updateStatus(AppStatus.mediaKitReady);
|
||||
|
||||
lifecycle.updateStatus(AppStatus.playerInitializing);
|
||||
PlaybackService().init();
|
||||
debugPrint('⏱️ T5 Player 完成: ${stopwatch.elapsedMilliseconds}ms');
|
||||
lifecycle.updateStatus(AppStatus.playerReady);
|
||||
|
||||
lifecycle.updateStatus(AppStatus.webdavChecking);
|
||||
final hasCred = await WebDAVService.instance.loadCredentials();
|
||||
debugPrint('⏱️ T6 WebDAV 完成: ${stopwatch.elapsedMilliseconds}ms');
|
||||
if (hasCred) {
|
||||
lifecycle.updateStatus(AppStatus.webdavReady);
|
||||
} else {
|
||||
lifecycle.updateStatus(AppStatus.webdavMissing);
|
||||
}
|
||||
|
||||
lifecycle.updateStatus(AppStatus.fullReady);
|
||||
debugPrint('✅ 应用完全就绪: ${stopwatch.elapsedMilliseconds}ms');
|
||||
} catch (e) {
|
||||
debugPrint('❌ 初始化错误: $e');
|
||||
lifecycle.updateStatus(AppStatus.error);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user