UWP音乐播放器闲置后恢复时崩溃问题排查求助
UWP音乐播放器闲置恢复崩溃排查求助
我开发UWP音乐播放器已有多年,遇到一个棘手问题:应用闲置一段不确定时长后恢复前台时会崩溃,但播放器持续播放时运行完全正常。崩溃无法稳定复现,多数情况下会出现,触发时长也不固定。
我尝试过这些排查手段,但没找到原因:
- 附加调试器时不会触发崩溃
- 已在
App类的Resuming、Suspending和UnhandledException方法中添加try-catch块与日志,但捕获不到有效信息
恳请提供后续排查步骤、诊断方法或避免此类崩溃的建议。
相关代码片段
MusicPlayer类部分代码
public class MusicPlayer : IMusicEventListener { public static MediaPlaybackState PlaybackState => Player.PlaybackSession.PlaybackState; public static bool IsPlaying => PlaybackState == MediaPlaybackState.Playing; public static Playlist NowPlaying => new Playlist(MenuFlyoutHelper.NowPlaying, CurrentPlaylist); private static MediaPlaybackList PlaybackList = new MediaPlaybackList() { MaxPlayedItemsToKeepOpen = 1 }; public static MediaPlayer Player = new MediaPlayer() { Source = PlaybackList }; }
App类部分代码
sealed partial class App : Application { public static bool Inited { get; private set; } = false; public static List<Action> LoadedListeners = new List<Action>(); public App() { this.InitializeComponent(); this.Suspending += OnSuspending; this.Resuming += App_Resuming; this.UnhandledException += App_UnhandledException; } private void App_Resuming(object sender, object e) { Debug.WriteLine($"sender {sender.GetType()} e {e}"); } protected override void OnLaunched(LaunchActivatedEventArgs e) { OnLaunched(e, null); } private void OnSuspending(object sender, SuspendingEventArgs e) { var deferral = e.SuspendingOperation.GetDeferral(); try { TileHelper.ResumeTile(); ToastHelper.HideToast(); Save(); SQLHelper.ClearInactive(); } catch (Exception ex) { Log.Warn($"OnSuspending exception {ex}"); } deferral.Complete(); } public static async void Save() { SettingsHelper.Save(); MusicPlayer.Save(); await Helper.ClearBackups(10); } protected override void OnBackgroundActivated(BackgroundActivatedEventArgs args) { var deferral = args.TaskInstance.GetDeferral(); switch (args.TaskInstance.Task.Name) { case ToastHelper.ToastTaskName: if (args.TaskInstance.TriggerDetails is Windows.UI.Notifications.ToastNotificationActionTriggerDetail details) { // Perform tasks if (Enum.TryParse(typeof(ToastButtonEnum), details.Argument, out object toastButton)) { try { HandleBackgroundActivation((ToastButtonEnum)toastButton); } catch (Exception e) { Log.Warn($"HandleBackgroundActivation failed {e}"); } } } break; } deferral.Complete(); } private async void HandleBackgroundActivation(ToastButtonEnum toastButton) { switch (toastButton) { case ToastButtonEnum.Next: MusicPlayer.MoveNext(); if (MusicPlayer.PlayMode == PlayMode.RepeatOne) { ToastHelper.HideToast(); await ToastHelper.ShowToast(MusicPlayer.CurrentMusic, MusicPlayer.PlaybackState); } break; case ToastButtonEnum.Pause: MusicPlayer.Pause(); break; case ToastButtonEnum.Play: MusicPlayer.Play(); break; case ToastButtonEnum.SwitchLyricsSourceToMusic: await ToastHelper.SwitchLyricsSource(LyricsSource.Music); break; case ToastButtonEnum.SwitchLyricsSourceToLrcFile: await ToastHelper.SwitchLyricsSource(LyricsSource.LrcFile); break; case ToastButtonEnum.SwitchLyricsSourceToInternet: await ToastHelper.SwitchLyricsSource(LyricsSource.Internet); break; } } protected override async void OnFileActivated(FileActivatedEventArgs args) { base.OnFileActivated(args); Music music = await Music.LoadFromPathAsync(args.Files[0].Path); Log.Info("activate file " + args.Files[0].Path); if (music == null) { Helper.ShowButtonedNotification("CannotReadLocalMusicFile", "Authorize", async (n) => { await StorageHelper.AuthorizeFolder(); }); return; } if (args.PreviousExecutionState == ApplicationExecutionState.Running) { MusicPlayer.AddNextAndPlay(music); } else { OnLaunched(null, music); } } private void App_UnhandledException(object sender, Windows.UI.Xaml.UnhandledExceptionEventArgs e) { Log.Error("App_UnhandledException {0}", e.Exception); } }
排查建议与步骤
1. 启用系统级崩溃日志捕获
UWP应用在调试器未附加时的崩溃,可通过Windows事件查看器获取详细日志:
- 打开事件查看器,导航到
Windows日志 -> 应用程序 - 筛选来源为
Application Error和Windows Error Reporting的事件,找到对应崩溃记录,查看异常代码、故障模块等信息
2. 补充异步代码的错误捕获
注意到Save()是async void方法,这类方法的异常无法被上层try-catch捕获,需改为async Task并在调用处用await+try-catch包裹:
// 修改Save方法 public static async Task Save() { SettingsHelper.Save(); MusicPlayer.Save(); await Helper.ClearBackups(10); } // 在OnSuspending中调用 try { TileHelper.ResumeTile(); ToastHelper.HideToast(); await Save(); // 改为await并捕获异常 SQLHelper.ClearInactive(); } catch (Exception ex) { Log.Warn($"OnSuspending exception {ex}"); }
同理,HandleBackgroundActivation也是async void,建议改为async Task并在调用处处理异常。
3. 检查MediaPlayer相关资源的生命周期
闲置恢复时,MediaPlayer或MediaPlaybackList可能已被系统回收或资源失效:
- 在
Resuming事件中,检查Player和PlaybackList是否为null,或尝试重新初始化资源 - 监听
MediaPlayer的MediaFailed事件,捕获播放相关异常:
// 在MusicPlayer初始化时添加 Player.MediaFailed += (s, e) => { Log.Error($"MediaPlayer failed: {e.ErrorMessage}, {e.ExtendedErrorCode}"); }; PlaybackList.CurrentItemChanged += (s, e) => { if (e.Error != null) { Log.Error($"PlaybackList item error: {e.Error.Message}"); } };
4. 排查UI线程与后台线程的冲突
恢复前台时,若后台线程正在操作UI相关资源,可能导致崩溃:
- 确保所有UI操作都在UI线程执行,使用
Dispatcher.RunAsync调度 - 在
Resuming事件中,延迟执行复杂操作,给UI线程留出初始化时间
5. 模拟应用挂起与恢复
使用Visual Studio的生命周期工具模拟挂起/恢复:
- 在调试工具栏中找到
生命周期事件按钮,选择Suspend和Resume,多次重复操作尝试复现崩溃 - 若能复现,可结合断点逐步排查问题
6. 检查静态资源的有效性
MusicPlayer中的静态字段(如Player、PlaybackList)在应用挂起后可能失效:
- 避免使用过多静态持有资源,考虑在
Resuming时重新初始化这些静态对象 - 检查
NowPlaying属性返回的Playlist是否依赖了已失效的UI元素(MenuFlyoutHelper.NowPlaying)
内容的提问来源于stack exchange,提问作者Seaky Lone
相关产品推荐
相关产品推荐

