AlarmManager 在 API 24 上运行但在 API 28 上不运行

AlarmManager funcions on API 24 but not on API 28

我正在使用机器人 AlarmManager 每隔 5 分钟触发一次事件。这在我的 api 24 phone 上运行良好,但在我的 api 28 phone 上无法运行。当 api 上的 运行 28 phone dumpsys alarm 显示警报正在响起,但从未调用其意图中传递的 JobIntentService

到目前为止,我已经尝试了不同的设置闹钟的方法,例如setAndAllowWhileIdle,但没有任何效果。 运行 dumpsys alarm on the api 28 phone 显示单发 setAlarmWhileIdle 熄灭但没有按预期再次熄灭。同样,setInexactRepeating 也会在传递的重复间隔处熄灭。两者都不调用传递的 JobIntentService.

public class DataUploadService extends JobIntentService
    implements DataClientManager.OnDataChangedListener {

private static final String TAG = "DataUploadService";
private static void LOG(String msg) { Log.d(TAG,msg); }
private static void LOGE(String msg) { Log.e(TAG,msg); }

public static final int DELAY_PERIOD = 5;

public static void initialize(Context context) {

    //parse minutes to get exact time
    final Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.add(Calendar.MINUTE,DELAY_PERIOD);

    DataUploadService.setAlarm(context,calendar.getTimeInMillis());

}

public static void setAlarm(Context context, long millis) {

    clearAlarm(context);

    AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

    Intent intent = new Intent(context, DataUploadService.class);
    PendingIntent alarmIntent = PendingIntent.getService(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, millis,
            DELAY_PERIOD * 60 * 1000, alarmIntent);

}

public static void clearAlarm(Context context) {
    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);

    Intent intent = new Intent(context, DataUploadService.class);
    PendingIntent alarmIntent = PendingIntent.getService(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);

    alarmManager.cancel(alarmIntent);
}

@Override
protected void onHandleWork(Intent intent) {

    LOGE("Data Acquisition Service has been called");
    final Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    LOG(calendar.getTime().toString());

}
}

在我的 MainActivity 中,我调用 DataUploadService.initalize(this),之后我希望每隔 5 分钟在我的系统日志中看到 "Data Acquisition Service has been called"。这在我的 api 24 phone 上按需发生,但在我的 api 28 phone 上却没有。我在文档中看到 api 与 AlarmManager 没有明显区别。

更新:

我发现 set(int type, long triggerAtMillis, String tag, AlarmManager.OnAlarmListener listener, Handler targetHandler) 可以在我的 api 28 级设备上使用。 null 和非 null Handler 似乎都有效。这并没有回答我为什么 JobIntentService 不再有效但确实提供了临时解决方法的问题。

我猜问题的发生是因为您正在安排警报并在它过期时启动服务。

由于服务是后台任务,现在它们受到更多限制(来自 API26+)

所以,我看到了两个解决方案:

第一个解决方案

创建广播接收器(将意图添加到 AndroidManifest.xml 等)。因此,5 分钟后,该广播将收到意图,该广播将通过以下方式启动服务:

AndroidManifest

<receiver
    android:name=".DataUploadBroadcast"
    android:exported="true"
    tools:ignore="ExportedReceiver">
    <intent-filter>
        <action android:name="MY_CUSTOM_ACTION" />
    </intent-filter>
</receiver>

广播接收器

public class DataUploadBroadcast extends BroadcastReceiver {

    public static final int DELAY_PERIOD = 5;

    public void onReceive(Context receivedContext, Intent intent) {
        if("MY_CUSTOM_ACTION".equals(intent.getAction() {
            DataUploadService.enqueueWork(context, new Intent(context, DataUploadService.class);
        }
    }

    private static void setAlarm(Context context, long millis) {
        clearAlarm(context);
        AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);

         // Note the class is different now
        Intent intent = new Intent(context, DataUploadBroadcast.class);

        // Set action
        intent.setAction("MY_CUSTOM_ACTION");

        // Note the getBroadcast
        PendingIntent alarmIntent = PendingIntent.getBroadcast(context, 1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP, millis, DELAY_PERIOD * 60 * 1000, alarmIntent);
    }

    public static void initialize(Context context) {
        // call setAlarm
    }

    private static void clearAlarm(Context context) {
        // Clear the alarm
    }
}

数据上传服务

private static final int JOB_ID = 100;

public static void enqueueWork(Context context, Intent work) {
    // This is how you should start the job.. and not PendingIntent.getService(...)
    enqueueWork(context, DataUploadService.class, JOB_ID, work);
}

@Override
protected void onHandleWork(Intent intent) {
    LOGE("Data Acquisition Service has been called");
}

第二种解法

让服务一收到Activity请求就启动。然后,在 onHandleWork 期间,您让服务休眠 5 分钟:

public class DataUploadService extends JobIntentService
    implements DataClientManager.OnDataChangedListener {

    public static final int DELAY_PERIOD = 5;
    private static final int JOB_ID = 100;

    public static void initialize(Context context) {
        enqueueWork(context, DataUploadService.class, JOB_ID, new Intent(context, DataUploadService.class));
    }

    @Override
    protected void onHandleWork(Intent intent) {
        try {
            Thread.sleep(DELAY_PERIOD * 60 * 1000);
            efectivellyExecuteWork()
        } catch (InterruptedException e) {
            LOGE("ERROR");
        }
}

private void efectivellyExecuteWork() {
    LOGE("Data Acquisition Service has been called");
    final Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    LOG(calendar.getTime().toString());
}

在第二个解决方案中,我只是让线程休眠 5 分钟。如果您需要更新该计时器,您可以添加一些逻辑,以防用户在 activity 到期之前打开它等。 例如,您可以存储带有未来日期的日历实例。这样,线程会休眠直到达到该日期。这将允许您在必要时更新日历日期(就像 clearing/recreating 事件);

类似于:

try {
    while(futureDate.getTimeInMillis() > Calendar.getInstance.getTimeInMillis()) {
        Thread.sleep(futureDate.getTimeInMillis() - Calendar.getInstance.getTimeInMillis());
    }
    efectivellyExecuteWork()
} catch (InterruptedException e) {
    LOGE("ERROR");
}