You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 18:24:51