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");
}
我正在使用机器人 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");
}