Статьи

Планирование повторяющихся задач в Android

Несколько распространенный вариант использования приложений для Android — запускать их при запуске телефона и периодически выполнять какой-то фрагмент кода.

Звучит просто, но есть некоторые подводные камни, поэтому вот несколько шагов, которые необходимо предпринять, чтобы достичь этого. Начните с тривиальных вещей:

В вашем манифесте вам нужно следующее, чтобы иметь возможность получить событие загрузки:

1
2
<uses-permission
    android:name="android.permission.RECEIVE_BOOT_COMPLETED" />

Затем, снова в манифесте, вам нужно два получателя:

01
02
03
04
05
06
07
08
09
10
11
12
<receiver android:name=".StartupLauncher">
  <intent-filter>
    <action android:name="android.intent.action.BOOT_COMPLETED" />
    <action android:name="android.intent.action.QUICKBOOT_POWERON" />
    <category android:name="android.intent.category.DEFAULT" />
  </intent-filter>
</receiver>
<receiver android:name=".Notifier">
  <intent-filter>
    <action android:name="com.yourpackage.HOURLY_CHECK" />
  </intent-filter>
</receiver>

Что это все? Вы увидите, что такое HOURLY_CHECK через минуту. Записи в .StartupLauncher я собрал из нескольких ответов StackOverflow, утверждают, что работают на нескольких устройствах. То, что вы обычно должны потреблять, это просто BOOT_COMPLETED , но BOOT_COMPLETED на всякий случай. Обратите внимание на атрибут enabled приемника — они обычно не нужны, но убедитесь, что получение не отключено родительским приложением. Честно говоря, я бы не советовал ставить бесполезные вещи «на всякий случай», но, учитывая причуды андроид-устройств, я бы их сохранил.

Теперь BOOT_COMPLETED Сервис, который получит событие BOOT_COMPLETED :

1
2
3
4
5
6
7
8
9
public class StartupLauncher extends BroadcastReceiver {
  @Override
  public void onReceive(final Context ctx, Intent intent) {
    AlarmManager alarmManager = (AlarmManager) ctx.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent("com.yourpackage.HOURLY_CHECK");
    PendingIntent notificationIntent = PendingIntent.getBroadcast(ctx.getApplicationContext(), 1, intent, 0);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, 0, TimeUnit.MILLISECONDS.convert(60, TimeUnit.MINUTES), notificationIntent);
  }
}

Почему бы не сделать это с ScheduledExecutorService ? Потому что как только вы запланируете Runnable , ваш BroadcastReceiver умирает, а исполнитель умирает вместе с ним, поэтому этот runnable никогда не вызывается.

Теперь у вас есть запланированный сигнал тревоги, где вы выполняете свою фактическую логику:

1
2
3
4
5
6
public class Notifier extends BroadcastReceiver {
  @Override
  public void onReceive(Context ctx, Intent intent) {
    // ...
  }
}

Конечно, вы можете иметь один BroadcastReceiver и различать в зависимости от цели, но я думаю, что это чище.

В целом, этот процесс может быть сложным — как это обычно бывает с Android, к сожалению.