Android:AlarmManager触发后如何显示弹窗并取消闹钟?
好的,我来帮你实现这个带取消功能的闹钟弹窗!你现在的代码已经完成了闹钟的设置和触发时的铃声播放,接下来只需要调整触发逻辑,用一个透明主题的Activity来承载弹窗(因为BroadcastReceiver里不能直接创建Dialog,它没有UI上下文),这样就能实现点击取消按钮关闭闹钟的功能了。
具体实现步骤
1. 配置透明主题的Activity
首先在AndroidManifest.xml里注册一个专门用来显示弹窗的Activity,给它设置透明主题:
<activity android:name=".AlarmDialogActivity" android:theme="@style/TransparentActivityTheme" android:excludeFromRecents="true" android:launchMode="singleInstance"> </activity>
然后在res/values/styles.xml中添加这个透明主题:
<style name="TransparentActivityTheme" parent="Theme.AppCompat.Light.NoActionBar"> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowIsTranslucent">true</item> <item name="android:windowNoTitle">true</item> <item name="android:windowAnimationStyle">@null</item> </style>
2. 编写弹窗Activity的核心逻辑
创建AlarmDialogActivity.java,这里负责显示弹窗、播放铃声,以及处理取消闹钟的逻辑:
public class AlarmDialogActivity extends AppCompatActivity { private MediaPlayer mediaPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 初始化并循环播放铃声 mediaPlayer = MediaPlayer.create(this, Settings.System.DEFAULT_RINGTONE_URI); mediaPlayer.setLooping(true); mediaPlayer.start(); // 显示带取消按钮的弹窗 new AlertDialog.Builder(this) .setTitle("闹钟提醒") .setMessage("该起床啦!点击取消关闭闹钟") .setPositiveButton("取消闹钟", (dialog, which) -> { // 停止并释放铃声资源 if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.stop(); mediaPlayer.release(); } // 根据传入的Uri取消对应的闹钟 cancelAlarm(getIntent().getData()); // 关闭弹窗Activity finish(); }) .setCancelable(false) // 禁止点击外部关闭弹窗,必须操作按钮 .show(); } private void cancelAlarm(Uri alarmUri) { AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this, MyAlarm.class); intent.setData(alarmUri); // 注意:这里的PendingIntent参数必须和设置闹钟时完全一致,才能正确匹配并取消 int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, flags); if (pendingIntent != null) { alarmManager.cancel(pendingIntent); pendingIntent.cancel(); } } @Override protected void onDestroy() { super.onDestroy(); // 确保退出时释放MediaPlayer,避免内存泄漏 if (mediaPlayer != null) { mediaPlayer.release(); mediaPlayer = null; } } }
3. 修改广播接收器的触发逻辑
原来的MyAlarm广播只播放铃声,现在改成启动上面的弹窗Activity,同时把闹钟的Uri传过去,方便后续取消:
public class MyAlarm extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 启动弹窗Activity,必须添加FLAG_ACTIVITY_NEW_TASK标志(广播中启动Activity的要求) Intent dialogIntent = new Intent(context, AlarmDialogActivity.class); dialogIntent.setData(intent.getData()); dialogIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(dialogIntent); } }
4. 优化设置闹钟的代码(可选但重要)
调整setAlarm方法中的PendingIntent标志,适配Android 12及以上版本的要求:
public void setAlarm(long timeInMillis){ if(Build.VERSION.SDK_INT >= 23){ // 修正Calendar的设置顺序(年、月、日、时、分、秒) mCalendar.set(Calendar.YEAR, mCalendar.get(Calendar.YEAR)); mCalendar.set(Calendar.MONTH, mCalendar.get(Calendar.MONTH)); mCalendar.set(Calendar.DAY_OF_YEAR, mCalendar.get(Calendar.DAY_OF_YEAR)); mCalendar.set(Calendar.HOUR_OF_DAY, mCalendar.get(Calendar.HOUR_OF_DAY)); mCalendar.set(Calendar.MINUTE, mCalendar.get(Calendar.MINUTE)); mCalendar.set(Calendar.SECOND, 0); // 加上秒数,确保触发时间精准 } final AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE); Intent intent = new Intent(this, MyAlarm.class); intent.setData(currentUri); // 根据Android版本选择合适的PendingIntent标志 int flags = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : 0; final PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, flags); alarmManager.setExact(AlarmManager.RTC, timeInMillis, pendingIntent); }
几个关键注意点
- PendingIntent匹配规则:取消闹钟时,必须使用和设置时完全相同的
Intent(包括Data、Action等)以及相同的requestCode,否则无法找到对应的闹钟并取消。 - 权限适配:Android 12及以上版本需要申请
SCHEDULE_EXACT_ALARM权限,否则setExact方法可能无法正常工作。 - 资源释放:一定要在Activity销毁时释放MediaPlayer,避免内存泄漏。
内容的提问来源于stack exchange,提问作者newbieHere
相关产品推荐
相关产品推荐

