После того, как в Android 8 был введен режим ожидания, Android наложил много ограничений на фоновую работу, что заставляет приложение вести себя непреднамеренно.
В одном из моих приложений я использовал выбор уровня заряда батареи каждые 15минут и подайте сигнал тревоги, если уровень заряда батареи превысил желаемый уровень заряда батареи, установленный пользователем в моем приложении.Я использовал следующий код для выполнения этой задачи.
установить повторяющуюся сигнализацию с помощью AlarmManagerApi:
public static void setAlarm(final Context context) {
final AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (manager != null) {
manager.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, 0, ALARM_INTERVAL,
getAlarmPendingIntent(context));
}
}
, где метод getAlarmPendingIntent выглядит следующим образом:
private static PendingIntent getAlarmPendingIntent(final Context context) {
return PendingIntent.getBroadcast(context, REQUEST_CODE, new Intent(context, AlarmReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
}
Настройка aBroadcastReciever для получения аварийных сигналов: Запускает службу на переднем плане для выполнения необходимой фоновой работы
public class AlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(final Context context, final Intent intent) {
if (context == null || intent == null) {
return;
}
if (intent.getAction() == null) {
Intent monitorIntent = new Intent(context, TaskService.class);
monitorIntent.putExtra(YourService.BATTERY_UPDATE, true);
ContextCompat.startForegroundService(context, monitorIntent);
}
}
}
TaskService (служба, выполняющая некоторую желаемую задачу) выглядит следующим образом
public class TaskService extends Service {
private static final String CHANNEL_ID = "channel_01";
private static final int NOTIFICATION_ID = 12345678;
@Override
public void onCreate() {
super.onCreate();
createNotificationChannel()
}
public void createNotificationChannel(){
// create notification channel for notifications
NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.app_name);
NotificationChannel mChannel =
new NotificationChannel(CHANNEL_ID, name, NotificationManager.IMPORTANCE_DEFAULT);
if (mNotificationManager != null) {
mNotificationManager.createNotificationChannel(mChannel);
}
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
startForeground(NOTIFICATION_ID, getNotification());
BatteryCheckAsync batteryCheckAsync = new BatteryCheckAsync();
batteryCheckAsync.execute();
return START_STICKY;
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
private Notification getNotification() {
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
.setContentText(text)
.setContentTitle("fetching battery level")
.setOngoing(true)
.setPriority(Notification.PRIORITY_LOW)
.setSmallIcon(R.mipmap.ic_launcher)
.setWhen(System.currentTimeMillis());
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
builder.setChannelId(CHANNEL_ID);
}
return builder.build();
}
private class BatteryCheckAsync extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... arg0) {
IntentFilter ifilter = new IntentFilter(Intent.ACTION_BATTERY_CHANGED);
Intent batteryStatus = YourService.this.registerReceiver(null, ifilter);
int level = batteryStatus.getIntExtra(BatteryManager.EXTRA_LEVEL, -1);
int scale = batteryStatus.getIntExtra(BatteryManager.EXTRA_SCALE, -1);
Log.i("Battery Charge Level",String.valueOf((level / (float) scale)));
return null;
}
protected void onPostExecute() {
YourService.this.stopSelf();
}
}
}
Задача BatteryCheckAsync получает уровень заряда батареи из фона и показывает текущий уровень заряда батареи в журналах.
Теперь, если я запусту приложение и проведу пальцем по приложению из моего списка недавних приложений и выключу экран устройства.
Приложение печатает журналы с уровнем заряда батареи в версиях Android до Android 8.Но после android 8 это перестало работать в течение нескольких минут после удаления приложения из моего недавнего списка приложений и выключения экрана устройства.Вышеуказанное поведение было ожидаемым, так как режим Doze был введен в Android, и тревоги отложены до следующего окна обслуживания.
============================================================================
ИзДокументация Android на https://developer.android.com/training/monitoring-device-state/doze-standby, Я обнаружил, что задокументировано, что
Стандартные тревоги AlarmManager (включая setExact () и setWindow ()) откладываются до следующего окна обслуживания.
Если вам нужно установить будильник, который срабатывает в режиме Doze, используйте setAndAllowWhileIdle () или setExactAndAllowWhileIdle ().
Сигналы тревоги, установленные с помощью setAlarmClock (), продолжают работать нормально - система выходит из режима Doze незадолго до срабатывания этих сигналов тревоги.
Итак, я изменил свой код следующим образом:
public static void setAlarm(final Context context) {
final AlarmManager manager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (manager == null) {
return;
}
int SDK_INT = Build.VERSION.SDK_INT;
if (SDK_INT < Build.VERSION_CODES.KITKAT) {
manager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, ALARM_INTERVAL, getAlarmPendingIntent(context));
} else if (SDK_INT < Build.VERSION_CODES.M) {
manager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, ALARM_INTERVAL, getAlarmPendingIntent(context));
} else {
manager.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME_WAKEUP, ALARM_INTERVAL, getAlarmPendingIntent(context));
}
}
вместо того, чтобы устанавливать повторяющуюся тревогу, я установил точную однократную тревогу и сам планировал следующую, обрабатывая каждую доставку тревоги в методе onRecieve для широковещательного приемника, выполнив выше setAlarm () метод снова.
Теперь, если я запустил приложение и смахнул приложение с моего списка недавних приложений на OnePlus 3 и выключил экран устройства, приложение печатает журналы в течение 40-45 минут, а затем внезапнослужба аварийной сигнализации и переднего плана перестает работать.
Есть ли у кого-нибудь решение для гарантированного выполнения периодической фоновой задачи в течение неопределенного периода времени в Android Oreo и выше?Я тестирую его на устройствах OnePlus с установленным Android Oreo или выше.