Я создал тип социального приложения и распространил его среди большой базы тестеров (~ 10000).
Медные трюки, помимо типичного жизненного цикла, я также хочу, чтобы он однажды выполнял сетевое действиекаждый час.Для этого я реализовал AlarmManager
, который получает WakeLock
и запускает ForegroundService
.После завершения действия служба останавливается и WakeLock
освобождается.Пока все хорошо.
Единственная проблема: она не работает для большого количества устройств, и я имею в виду что-то вроде 80% из них.ForegroundService
либо останавливается в середине задачи, либо не запускается вообще.Я запускал его на всех эмуляторах и на некоторых персональных устройствах (Android 6.0 до 8.1), и он отлично работает.Длительное тестирование показало, что режим Doze здесь не проблема, прежде чем кто-либо спросит.
Я считаю, что проблема заключается в моей плохой реализации одного из 3 ресурсов, которые либо приводят к гибели ForegroundService
,AlarmManager
для очистки или WakeLock
для отказа.
Все исключения регистрируются, и они почти не существуют.Я также не получаю никаких сбоев / ANR.
Исходя из того, как я пытаюсь их реализовать, я ожидаю, что мой AlarmManager
всегда будет планировать сигналы тревоги, как после загрузки, так и после обновления приложения.Любая помощь будет принята с благодарностью, вот соответствующий код:
Самый первый вызов для запуска AlarmManager
- это метод onPause()
моей основной деятельности.
MainActivity.java:
@Override
protected void onPause() {
super.onPause();
Updater.call(this);
}
Updater.java:
static void call(Context context) {
if (!UpdaterService.active) {
UpdaterService.acquireWakeLock(context);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
context.startForegroundService(new Intent(context.getApplicationContext(), UpdaterService.class));
else
context.startService(new Intent(context.getApplicationContext(), UpdaterService.class));
}
try {
Intent intent = new Intent(context.getApplicationContext(), UpdaterBroadcastReceiver.class);
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
context.getApplicationContext(), 54321, intent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
try {
alarmManager.cancel(pendingIntent);
} catch (Exception e) {
logException(e);
}
if (Build.VERSION.SDK_INT < 23)
alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (3600 * 1000), pendingIntent);
else
alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + (3600 * 1000), pendingIntent);
} catch (Exception e) {
UpdaterService.releaseWakeLock();
logException(e);
}
}
UpdaterService.java:
public class UpdaterService extends Service {
static boolean active = false;
private static PowerManager.WakeLock wl;
@Override
public IBinder onBind(Intent intent) {
return new Binder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
active = true;
startForeground(1423, getNotification());
// A pseudocode method which begins async networking work. Eventually, the other thread will call stopSelf() once the work is complete.
Update();
return START_NOT_STICKY;
}
@Override
public void onCreate() {
super.onCreate();
active = true;
}
private Notification getNotification() {
NotificationChannel channel;
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationManager mNotificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE);
channel = new NotificationChannel(
"1423",
"Updater Notification",
NotificationManager.IMPORTANCE_DEFAULT
);
channel.setSound(null, null);
channel.enableVibration(false);
mNotificationManager.createNotificationChannel(channel);
}
} catch (Exception e) {
logException(e);
}
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(this, "1423")
.setPriority(PRIORITY_DEFAULT)
.setSmallIcon(R.drawable.notification_icon)
.setAutoCancel(true)
.setChannelId("1423");
return mBuilder.build();
}
@Override
public void onDestroy() {
super.onDestroy();
releaseWakeLock();
active = false;
}
@Override
public void onTaskRemoved(Intent rootIntent) {
stopSelf();
}
static void acquireWakeLock(Context context) {
try {
if (wl == null) {
PowerManager powerManager = (PowerManager) context.getApplicationContext().getSystemService(Service.POWER_SERVICE);
wl = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Update WakeLock");
}
if (!wl.isHeld()) {
wl.acquire(600 * 1000);
}
} catch (Exception e) {
logException(e);
}
}
static void releaseWakeLock() {
try {
if (wl != null && wl.isHeld()) {
wl.release();
}
} catch (Exception e) {
logException(e);
}
}
}
UpdaterBroadcastReceiver.java:
public final class UpdaterBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Updater.call(context);
}
}
AndroidManifest.xml:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<service
android:name = ".UpdaterService"
android:enabled="true">
</service>
<receiver android:name=".UpdaterBroadcastReceiver" android:exported="true" android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<action android:name="android.intent.action.MY_PACKAGE_REPLACED" />
</intent-filter>
</receiver>