当手机不使用时,AlarmManager 重复警报会随机丢失

2024-03-08

我正在调用背景Service每隔30分钟读取当前位置的经纬度并通过POST API发送到服务器。

我在用setRepeating()的方法AlarmManager班级每 30 分钟安排一次闹钟。但有时警报会被错过,服务也不会被调用。为了监控每 30 分钟是否调用警报,我在 sdcard 中生成了 Log.txt 文件。对于每次调用的警报,当前时间的条目将写入 Log.txt 文件中。但在比较 4 到 5 个设备 Log.txt 文件后,我注意到某些设备的警报没有调用onCreate()的方法UserTrackingReceiver.java(后台服务)。下面提到的完整代码块。

当应用程序启动时registerUserTrackingReceiver()已调用方法如下:

public static void registerUserTrackingReceiver(Context context) {
        try {
            Intent intent = new Intent(context, UserTrackingReceiver.class);

            boolean alarmUp = (PendingIntent.getService(context, 1001, intent, PendingIntent.FLAG_NO_CREATE) == null);

            if (alarmUp) {
                Calendar calendar = Calendar.getInstance();

                if (calendar.get(Calendar.MINUTE) > 0 && calendar.get(Calendar.MINUTE) <= 30) {
                    calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY));
                    calendar.set(Calendar.MINUTE, 30);
                    calendar.set(Calendar.SECOND, 0);
                } else if (calendar.get(Calendar.MINUTE) > 30) {
                    if (calendar.get(Calendar.HOUR_OF_DAY) == 23) {
                        calendar.set(Calendar.HOUR_OF_DAY, 0);
                    } else {
                        calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1);
                    }
                    calendar.set(Calendar.MINUTE, 0);
                    calendar.set(Calendar.SECOND, 0);
                } else {
                    calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY));
                    calendar.set(Calendar.MINUTE, 0);
                    calendar.set(Calendar.SECOND, 0);
                }

                PendingIntent sender = PendingIntent.getService(context, 1001, intent, 0);
                AlarmManager alarmManager = (AlarmManager) context.getSystemService(context.ALARM_SERVICE);
                alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(),
                        AlarmManager.INTERVAL_HALF_HOUR, sender);
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
}

UserTrackingReceiver.java在下面:

public class UserTrackingReceiver extends Service
        implements LocationListener,
        GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener {

    @Override
    public void onCreate() {
        super.onCreate();

        Calendar calendar = Calendar.getInstance();
        Util.appendLog("Tracking Alarm Called on: " + calendar.get(Calendar.HOUR_OF_DAY) + " : " + calendar.get(Calendar.MINUTE) + " : " + calendar.get(Calendar.SECOND));
        stopSelf();
    }
}

In 实用工具.javaappendLog()方法如下:

public static void appendLog(String text) {

        String baseDir = Environment.getExternalStorageDirectory().getAbsolutePath();

        File logFile = new File(baseDir + "/" + Constant.AppNameSuper + "/log.txt");
        if (!logFile.exists()) {
            try {
                logFile.createNewFile();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        try {
            //BufferedWriter for performance, true to set append to file flag
            BufferedWriter buf = new BufferedWriter(new FileWriter(logFile, true));
            buf.append(text);
            buf.newLine();
            buf.close();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
}

如果警报按照上述代码每 30 分钟调用一次,则应将其写入 SDCARD 中的 Log.txt 文件中。但问题是它无法每 30 分钟写入一次日志文件,这意味着警报丢失。根据两天的阅读,我注意到白天由于用户连续使用手机而不会丢失警报,但在晚上不使用手机时警报会丢失。

不同设备的输出日志文件如下:

设备A日志.txt

  • 跟踪报警调用时间:0 : 0 : 31(从晚上 12:00 开始)
  • 调用跟踪警报:1 : 10 : 27
  • 跟踪警报调用时间:3 : 5 : 25
  • 呼叫跟踪警报:6 : 55 : 31
  • 调用跟踪警报:7 : 0 : 6
  • 调用跟踪警报:7 : 30 : 0
  • 调用跟踪警报:8 : 0 : 6
  • 调用跟踪警报:8 : 30 : 0
  • 调用跟踪警报:9 : 0 : 6
  • 调用跟踪警报:9 : 30 : 0
  • 调用跟踪警报:10 : 0 : 0

设备B日志.txt

  • 跟踪报警调用时间:0 : 0 : 27(从晚上 12:00 开始)
  • 调用跟踪警报:0 : 30 : 1
  • 调用跟踪警报:1 : 0 : 1
  • 调用跟踪警报:1 : 30 : 2
  • 调用跟踪警报:2 : 0 : 1
  • 调用跟踪警报:2 : 30 : 1
  • 调用跟踪警报:3 : 0 : 1
  • 调用跟踪警报:3 : 30 : 1
  • 调用跟踪警报:4 : 0 : 1
  • 调用跟踪警报:4 : 30 : 29
  • 调用跟踪警报:5 : 0 : 1
  • 调用跟踪警报:5 : 30 : 2
  • 调用跟踪警报:6 : 0 : 30
  • 调用跟踪警报:6 : 30 : 1
  • 调用跟踪警报:7 : 0 : 1
  • 调用跟踪警报:7 : 30 : 1
  • 调用跟踪警报:8 : 0 : 1
  • 调用跟踪警报:8 : 30 : 1
  • 调用跟踪警报:9 : 0 : 32
  • 调用跟踪警报:9 : 30 : 1

设备C日志.txt

  • 跟踪报警调用时间:0 : 0 : 7(从晚上 12:00 开始)
  • 调用跟踪警报:0 : 30 : 3
  • 调用跟踪警报:1 : 0 : 6
  • 调用跟踪警报:1 : 30 : 1
  • 调用跟踪警报:2 : 0 : 32
  • 调用跟踪警报:2 : 30 : 3
  • 调用跟踪警报:3 : 1 : 50
  • 调用跟踪警报:3 : 30 : 5
  • 调用跟踪警报:4 : 1 : 58
  • 调用跟踪警报:4 : 31 : 14
  • 调用跟踪警报:5 : 0 : 1
  • 调用跟踪警报:5 : 30 : 1
  • 调用跟踪警报:6 : 2 : 1
  • 调用跟踪警报:6 : 30 : 1
  • 调用跟踪警报:7 : 0 : 1
  • 调用跟踪警报:7 : 30 : 1
  • 调用跟踪警报:8 : 0 : 1
  • 调用跟踪警报:8 : 30 : 4
  • 呼叫跟踪警报:9 : 1 : 44
  • 调用跟踪警报:9 : 30 : 1

设备D日志.txt

  • 跟踪报警调用时间:0 : 1 : 25(从晚上 12:00 开始)
  • 调用跟踪警报:0 : 30 : 0
  • 调用跟踪警报:1 : 31 : 41
  • 呼叫跟踪警报:2 : 39 : 52
  • 调用跟踪警报:3 : 0 : 25
  • 呼叫跟踪警报:3 : 30 : 58
  • 调用跟踪警报:4 : 0 : 25
  • 呼叫跟踪警报:4 : 30 : 56
  • 呼叫跟踪警报:5 : 30 : 51
  • 呼叫跟踪警报:7 : 18 : 55
  • 调用跟踪警报:7 : 30 : 0
  • 调用跟踪警报:8 : 0 : 25
  • 呼叫跟踪警报:8 : 30 : 43
  • 调用跟踪警报:9 : 0 : 3
  • 呼叫跟踪警报:9 : 30 : 25
  • 调用跟踪警报:10 : 0 : 25
  • 调用跟踪警报:10 : 30 : 4
  • 呼叫跟踪警报:11 : 1 : 52
  • 呼叫跟踪警报:11 : 30 : 27
  • 调用跟踪警报:12 : 1 : 6⁠⁠⁠⁠

问题可能是你的PendingIntent呼叫一个Service。设备可以在您之前返回睡眠状态Service完成(甚至开始)执行。

我建议你使用BroadcastReceiver相反(因为WakeLock期间保证onReceive()).

获得一个WakeLock in onReceive(),开始你的Service从那里释放WakeLock来自Service, 在适当的时候。

为了简化这个过程,您可以使用WakefulBroadcastReceiver https://developer.android.com/reference/android/support/v4/content/WakefulBroadcastReceiver.html辅助类:

  1. Call PendingIntent.getBroadcast()代替PendingIntent.getService().
  2. 开始一个IntentService from onReceive()通过致电WakefulBroadcastReceiver.startWakefulService().
  3. 做你的事onHandleIntent()并打电话WakefulBroadcastReceiver.completeWakefulIntent()等结束了。

例如,一个BroadcastReceiver开始清醒Service:

public class ExampleReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent wakefulServiceIntent = new Intent(context,
            ExampleWakefulService.class);

        WakefulBroadcastReceiver.startWakefulService(context,
            wakefulServiceIntent);
    }
}

And the Service:

public class ExampleWakefulService extends IntentService {

    private static final String NAME = "com.example.ExampleWakefulService";

    public ExampleWakefulService() {
        super(NAME);
    }

    @Override
    protected void onHandleIntent(Intent intent) {

        // doing stuff

        WakefulBroadcastReceiver.completeWakefulIntent(intent);
    }
}

另外,请查看本文 https://developer.android.com/training/scheduling/wakelock.html来自开发人员指南中有关保持设备唤醒的内容。

On API 级别 23+你必须处理Doze.

来自文档 https://developer.android.com/training/monitoring-device-state/doze-standby.html:

为了帮助安排闹钟,Android 6.0(API 级别 23)引入了 两个新的AlarmManager方法:setAndAllowWhileIdle() and setExactAndAllowWhileIdle()。通过这些方法,您可以设置闹钟 即使设备处于打瞌睡状态也会触发。

不幸的是没有其他选择setRepeating(),所以你有两个选择:

  • 设置准确的警报(根据设备的 API 级别使用适当的方法,查看这个答案 https://stackoverflow.com/a/39739886/3363481例如)并在每次触发时重新安排它们。
  • 白名单 https://developer.android.com/training/monitoring-device-state/doze-standby.html#support_for_other_use_cases您的应用程序(不推荐,因为 Google 的严格修订政策)。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

当手机不使用时,AlarmManager 重复警报会随机丢失 的相关文章

随机推荐