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

Android Alarm Manager仅触发一次,无法每5分钟重复触发问题求助

问题分析与解决方案

你的Alarm Manager仅触发一次的核心原因是未在每次触发后重新调度下一次闹钟,同时存在几个关键的代码错误,以下是具体修复步骤:


1. 核心问题梳理

  • setExactAndAllowWhileIdle是单次闹钟,不会自动重复,必须在每次触发后手动设置下一次
  • MainActivity中给Intent Action添加了System.currentTimeMillis(),导致每次Intent唯一,无法匹配广播接收器的过滤规则
  • 广播接收器未处理BOOT_COMPLETED的过滤规则,重启后无法恢复闹钟
  • Android 13+需要动态申请SCHEDULE_EXACT_ALARM权限

2. 修复后的代码

MainActivity.java

private void setAlarm() {
    // 检查Android 13+的精确闹钟权限
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
        AlarmManager alarmManager = getSystemService(AlarmManager.class);
        if (!alarmManager.canScheduleExactAlarms()) {
            Intent intent = new Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM);
            startActivity(intent);
            return;
        }
    }

    AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(this, LocationAlarmReceiver.class);
    intent.setAction("com.mhz.evs.ACTION_FETCH_LOCATION"); // 固定Action,匹配广播过滤规则
    PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 
        PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);

    long interval = AlarmManager.INTERVAL_FIFTEEN_MINUTES / 3; // 5分钟间隔
    long triggerAtMillis = System.currentTimeMillis() + interval;

    if (alarmManager != null) {
        alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
    }
}

LocationAlarmReceiver.java

public class LocationAlarmReceiver extends BroadcastReceiver {
    private static final String ACTION_FETCH_LOCATION = "com.mhz.evs.ACTION_FETCH_LOCATION";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction() == null) return;

        switch (intent.getAction()) {
            case Intent.ACTION_BOOT_COMPLETED:
                // 设备重启后设置第一次闹钟
                setNextAlarm(context);
                break;
            case ACTION_FETCH_LOCATION:
                Toast.makeText(context, "Triggered", Toast.LENGTH_SHORT).show();
                Intent serviceIntent = new Intent(context, LocationService.class);
                
                // Android O+必须使用前台服务
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    context.startForegroundService(serviceIntent);
                } else {
                    context.startService(serviceIntent);
                }
                
                // 重新调度下一次闹钟
                setNextAlarm(context);
                break;
        }
    }

    // 封装设置下一次闹钟的逻辑
    private void setNextAlarm(Context context) {
        AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(context, LocationAlarmReceiver.class);
        intent.setAction(ACTION_FETCH_LOCATION);
        PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 
            PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT);

        long interval = AlarmManager.INTERVAL_FIFTEEN_MINUTES / 3;
        long triggerAtMillis = System.currentTimeMillis() + interval;

        if (alarmManager != null) {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
        }
    }
}

AndroidManifest.xml

<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM"/>
<uses-permission android:name="android.permission.USE_EXACT_ALARM"/>
<!-- Android O+前台服务权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>

<receiver 
    android:name=".Receivers.LocationAlarmReceiver" 
    android:exported="true" 
    android:enabled="true">
    <intent-filter>
        <action android:name="com.mhz.evs.ACTION_FETCH_LOCATION"/>
        <action android:name="android.intent.action.BOOT_COMPLETED"/> <!-- 添加BOOT_COMPLETED过滤 -->
    </intent-filter>
</receiver>

<service 
    android:name=".Services.LocationService" 
    android:exported="true" 
    android:enabled="true"/>

3. 关键修复说明

  1. 移除动态Action后缀:使用固定的Action字符串,确保广播接收器能正确匹配Intent
  2. 每次触发后重新调度:在onReceive中处理完定位逻辑后,调用setNextAlarm设置下一次闹钟
  3. 替换setRepeating:setRepeating在Android 6+的Doze模式下不可靠,改用setExactAndAllowWhileIdle保证精确触发
  4. 添加BOOT_COMPLETED过滤:确保设备重启后能重新初始化闹钟
  5. 动态权限申请:Android 13+需要用户授权精确闹钟权限,否则无法调度

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 13:02:34