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

点击通知停止AlarmManager闹钟播放的实现求助

解决点击通知停止闹钟播放的问题

我明白你现在的困扰——在BroadcastReceiver里直接创建MediaPlayer播放闹钟,不仅生命周期不稳定,还没法通过点击通知来控制它停止。咱们一步步来解决这个问题:

核心问题分析

你当前的代码里,MediaPlayer是onReceive方法里的局部变量,当onReceive执行完毕后,这个MediaPlayer会被系统回收,而且你没法从其他组件(比如点击通知打开的AddNoteActivity)访问到它,自然没法停止播放。另外,BroadcastReceiver不适合做长时间的音频播放操作,应该用Service来管理音频播放。

解决方案步骤

1. 创建一个Foreground Service来管理MediaPlayer

这个Service负责播放和停止闹钟音频,因为Foreground Service优先级更高,不容易被系统杀死,而且能跨组件控制。

public class AlarmSoundService extends Service {
    private MediaPlayer mediaPlayer;

    @Override
    public void onCreate() {
        super.onCreate();
        // 初始化MediaPlayer
        mediaPlayer = MediaPlayer.create(this, Settings.System.DEFAULT_RINGTONE_URI);
        mediaPlayer.setLooping(true); // 让闹钟循环播放,直到手动停止
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String action = intent.getAction();
        if (action != null) {
            switch (action) {
                case "PLAY_ALARM":
                    startForeground(101, createNotification()); // 前台服务必须显示通知
                    if (!mediaPlayer.isPlaying()) {
                        mediaPlayer.start();
                    }
                    break;
                case "STOP_ALARM":
                    stopAlarm();
                    stopForeground(true);
                    stopSelf();
                    break;
            }
        }
        return START_STICKY;
    }

    private Notification createNotification() {
        // 创建停止闹钟的PendingIntent
        Intent stopIntent = new Intent(this, AlarmSoundService.class);
        stopIntent.setAction("STOP_ALARM");
        PendingIntent stopPendingIntent = PendingIntent.getService(this, 0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

        // 构建通知
        return new NotificationCompat.Builder(this, NotificationHelper.chanel1ID)
                .setContentTitle("闹钟响了")
                .setContentText("点击停止闹钟")
                .setSmallIcon(R.drawable.ic_notifications_black_24dp)
                .setContentIntent(stopPendingIntent)
                .setAutoCancel(true)
                .build();
    }

    private void stopAlarm() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
            mediaPlayer.release();
            mediaPlayer = null;
        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stopAlarm();
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

2. 修改MyAlarm BroadcastReceiver

现在Receiver不再直接播放音频,而是启动上面的Service:

public class MyAlarm extends BroadcastReceiver {
    private static final int NOTIFICATION_ID = 101;

    @Override
    public void onReceive(final Context context, Intent intent) {
        // 启动播放闹钟的Service
        Intent serviceIntent = new Intent(context, AlarmSoundService.class);
        serviceIntent.setAction("PLAY_ALARM");
        serviceIntent.setData(intent.getData()); // 传递原有的Uri数据
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(serviceIntent);
        } else {
            context.startService(serviceIntent);
        }
    }
}

3. 更新NotificationHelper(可选,保留跳转到AddNoteActivity的功能)

如果你希望点击通知既停止闹钟又打开AddNoteActivity,可以创建一个组合PendingIntent,先执行停止操作再打开Activity:

// 在NotificationHelper中修改getChannel1Notification方法
public NotificationCompat.Builder getChannel1Notification(Context context, Uri uri) {
    // 停止闹钟的Intent
    Intent stopIntent = new Intent(context, AlarmSoundService.class);
    stopIntent.setAction("STOP_ALARM");
    PendingIntent stopPendingIntent = PendingIntent.getService(context, 0, stopIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

    // 打开AddNoteActivity的Intent
    Intent activityIntent = new Intent(context, AddNoteActivity.class);
    activityIntent.setData(uri);
    PendingIntent activityPendingIntent = PendingIntent.getActivity(context, 1, activityIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

    // 组合两个Intent:先停止闹钟,再打开Activity
    Intent[] combinedIntents = {stopIntent, activityIntent};
    PendingIntent combinedPendingIntent = PendingIntent.getActivities(context, 0, combinedIntents, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

    return new NotificationCompat.Builder(getApplicationContext(), chanel1ID)
            .setContentTitle("闹钟响了")
            .setContentText("点击停止并打开笔记")
            .setSmallIcon(R.drawable.ic_notifications_black_24dp)
            .setContentIntent(combinedPendingIntent)
            .setAutoCancel(true);
}

4. 声明权限和Service

在AndroidManifest.xml里添加必要的权限和组件声明:

<!-- Android 12及以上需要前台服务权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<!-- Android 13及以上需要通知权限 -->
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application ...>
    <service android:name=".AlarmSoundService" />
    <receiver android:name=".MyAlarm" />
    <!-- 你的其他组件声明 -->
</application>

关键说明

  • 使用Foreground Service是因为音频播放属于长时间操作,普通Service容易被系统回收,Foreground Service能保证播放稳定。
  • 通过给Service发送带Action的Intent来控制播放和停止,这样点击通知时就能触发停止操作。
  • 注意PendingIntent的Flag,Android 12+需要添加FLAG_IMMUTABLE或者FLAG_MUTABLE,避免兼容性问题。

这样修改后,点击通知就能停止闹钟播放了,而且音频播放的生命周期也更稳定。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:11:40