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

如何无需新建实例,更新PlayerNotificationManager的音频播放通知信息

问题分析

你的核心问题在于每次歌曲切换时都重新创建了整个PlayerNotificationManager实例,这完全没必要——ExoPlayer的PlayerNotificationManager设计就是用来长期复用的,重复创建不仅浪费系统资源,还可能导致通知闪烁、状态不一致等问题。

解决方案:复用PlayerNotificationManager实例

我们只需要初始化一次PlayerNotificationManager,然后在歌曲信息更新时,动态刷新通知内容即可。具体步骤如下:

  1. 将PlayerNotificationManager定义为类成员变量,确保全局唯一
  2. 自定义MediaDescriptionAdapter,让它能动态读取当前的歌曲标题和艺术家信息
  3. 在onNewStreamTitle回调中,更新歌曲信息并触发通知刷新

修改后的完整代码示例

首先,在你的Service/Activity中添加类成员变量:

private PlayerNotificationManager mPlayerNotificationManager;
private String currentSongTitle; // 存储当前歌曲名
private String currentArtist;    // 存储当前艺术家

然后,拆分通知逻辑为初始化和内容更新两个方法:

// 初始化通知(仅调用一次,比如播放器准备完成时)
void initExoPlayerNotification(Context context, SimpleExoPlayer exoPlayer) {
    if (mPlayerNotificationManager != null) {
        return; // 已初始化过,直接返回
    }

    mPlayerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
        context,
        "PRIMARY_CHANNEL_ID",
        R.string.plaza,
        NOTIFICATION_ID,
        new PlayerNotificationManager.MediaDescriptionAdapter() {
            @Override
            public String getCurrentContentTitle(Player player) {
                return currentArtist != null ? currentArtist : "未知艺术家";
            }

            @Nullable
            @Override
            public PendingIntent createCurrentContentIntent(Player player) {
                Intent intent = new Intent(service, MainActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_NEW_TASK);
                return PendingIntent.getActivity(service, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            }

            @Override
            public String getCurrentContentText(Player player) {
                return currentSongTitle != null ? currentSongTitle : "未知歌曲";
            }

            @Nullable
            @Override
            public Bitmap getCurrentLargeIcon(Player player, PlayerNotificationManager.BitmapCallback callback) {
                return BitmapFactory.decodeResource(service.getResources(), R.drawable.largeicon);
            }

            @Nullable
            @Override
            public String getCurrentSubText(Player player) {
                return null;
            }
        }
    );

    // 通知配置仅需设置一次
    mPlayerNotificationManager.setUseNavigationActions(false);
    mPlayerNotificationManager.setFastForwardIncrementMs(0);
    mPlayerNotificationManager.setRewindIncrementMs(0);
    mPlayerNotificationManager.setColorized(true);
    mPlayerNotificationManager.setColor(0xFFBDBDBD);
    mPlayerNotificationManager.setUseChronometer(true);
    mPlayerNotificationManager.setOngoing(true);
    mPlayerNotificationManager.setPriority(NotificationCompat.PRIORITY_MAX);
    mPlayerNotificationManager.setUsePlayPauseActions(true);
    mPlayerNotificationManager.setSmallIcon(R.drawable.smallwidth);
    mPlayerNotificationManager.setNotificationListener(new PlayerNotificationManager.NotificationListener() {
        @Override
        public void onNotificationStarted(int notificationId, Notification notification) {
            service.startForeground(notificationId, notification);
        }

        @Override
        public void onNotificationCancelled(int notificationId) {
            service.stopSelf();
            cancelNotify();
        }
    });

    mPlayerNotificationManager.setPlayer(exoPlayer);
}

// 更新通知内容(歌曲切换时调用)
void updateNotificationContent(String streamTitle) {
    // 解析歌曲名和艺术家,修复原代码的索引错误问题
    try {
        currentSongTitle = StringUtils.substringBefore(streamTitle, " - ");
        currentArtist = StringUtils.substringAfter(streamTitle, " - ");
    } catch (Exception e) {
        int dashIndex = streamTitle.indexOf(" - ");
        if (dashIndex != -1) {
            currentSongTitle = streamTitle.substring(0, dashIndex);
            currentArtist = streamTitle.substring(dashIndex + 3); // 跳过" - "分隔符
        } else {
            currentSongTitle = streamTitle;
            currentArtist = "未知艺术家";
        }
    }

    // 触发通知刷新,让PlayerNotificationManager重新读取最新元数据
    if (mPlayerNotificationManager != null) {
        mPlayerNotificationManager.incrementPlaybackState();
        // 也可以直接调用 notify() 强制刷新
        // mPlayerNotificationManager.notify();
    }
}

最后修改元数据监听回调,不再重建通知:

OnNewMetadataListener listener = new OnNewMetadataListener() { 
    @Override 
    public void onNewHeaders(String stringUri, List<String> name, List<String> desc, List<String> br, List<String> genre, List<String> info) { } 

    @Override 
    public void onNewStreamTitle(String stringUri, final String streamTitle) { 
        MainActivity.songinfo.setText(streamTitle); 
        // 只更新通知内容,不重建整个通知实例
        updateNotificationContent(streamTitle); 
    } 
}; 

// 在播放器初始化完成后调用一次通知初始化
initExoPlayerNotification(getApplicationContext(), exoPlayer);

AudiostreamMetadataManager.getInstance() 
    .setUri(uri) 
    .setOnNewMetadataListener(listener) 
    .setUserAgent(UserAgent.WINDOWS_MEDIA_PLAYER) 
    .start(); 

关键优化点说明

  • 复用核心实例:避免重复创建PlayerNotificationManager,减少系统资源消耗,同时保证通知状态稳定无闪烁。
  • 动态元数据读取:通过类成员变量存储最新歌曲信息,MediaDescriptionAdapter在需要时自动读取最新值,替代原代码中用final变量固定死的逻辑。
  • 容错逻辑修复:原代码的异常处理存在索引越界风险,现在优化了分隔符解析逻辑,确保解析失败时不会崩溃。

内容的提问来源于stack exchange,提问作者user7954210

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:01:22