Ошибка AlarmManager, ForegroundService или WakeLock на большинстве устройств - PullRequest
0 голосов
/ 19 сентября 2018

Я создал тип социального приложения и распространил его среди большой базы тестеров (~ 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>
...