web-dev-qa-db-fra.com

Un service de premier plan se fait tuer d'Oreo

J'ai écrit un service de premier plan qui fonctionne correctement pour toutes les versions de système d'exploitation inférieures à Oreo. Le processus de demande de Oreo est éliminé après 5 minutes de fermeture et de suppression de la demande.

Selon la documentation pour les développeurs Android concernant les limitations d'exécution background , le système d'exploitation ne doit pas tuer les applications pour lesquelles un service de premier plan est en cours d'exécution et la notification s'affiche dans la fenêtre de notification.

Selon les directives de la documentation du développeur. J'ai suivi les étapes ci-dessous pour démarrer le service de premier plan.

  1. Le service de premier plan est démarré avec la méthode startForegroundService()
  2. Dans les 5 secondes qui suivent le démarrage du service, une notification s'affiche pour le service à l'aide de startForeground()
  3. Retourne START_STICKY depuis onStartCommand() sur service

Je suis confronté à ce problème sur les téléphones suivants:

  1. OnePlus 5T
  2. Vivo
  3. Oppo
  4. Mi

Qu'est-ce que j'ai essayé d'empêcher la destruction du service de premier plan?

  1. Désactivez l'optimisation battery pour l'application en affichant la boîte de dialogue système À l'utilisateur afin de désactiver le mode assoupi.

Qu'est-ce que j'ai essayé de redémarrer le service de premier plan?

  1. Utilisé AlarmManager pour redémarrer le service à partir de onTaskRemoved () . Veuillez vérifier ce lien pour plus de détails.

D'après ce que j'ai compris, ces fabricants ont personnalisé l'AOSP et ne respectent pas les directives du système d'exploitation permettant le fonctionnement du service de premier plan. Peut-être que ces fabricants ont agi de la sorte en donnant aux utilisateurs une longue durée de vie de la batterie. 

Classe de service de premier plan

    class DataCaptureService : Service() {

        private var isServiceStarted = false

        override fun onBind(intent: Intent?): IBinder? {
            return null
        }

override fun onCreate() {
        super.onCreate()
        wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
                    newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WakelockTag123").apply {
                        acquire()
                    }
                }
    }


        override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
            val serviceAction = intent?.action
            LogUtils.logD("OnStartCommand(). Action=$serviceAction")
            if (Constants.INTENT.ACTION_STOP_SERVICE == serviceAction) {
                LogUtils.logD("Stopping data capture service")
                stopForeground(true)
                stopSelf()
            } else if (Constants.INTENT.ACTION_START_SERVICE == serviceAction && !isServiceStarted) {
                LogUtils.logD("Starting data capture service")
                isServiceStarted = true
                // Here showing notification using a utility method (startForeground(id, notification))
                createNotification(this)
                // Doing some stuff here
                ----------------------
                //
            }
            return START_STICKY
        }

        override fun onDestroy() {
            super.onDestroy()
            if (isServiceStarted) {
                LogUtils.logD("onDestroy of DataCaptureService method is invoked")
                // Doing some stuff here
                ----------------------
                //
                isServiceStarted = false
                if (wakeLock.isHeld) {
                    wakeLock.release()
                }
            }
        }

        override fun onTaskRemoved(rootIntent: Intent?) {
            LogUtils.logD("onTaskRemoved of DataCaptureService method is invoked")
            ensureServiceStaysRunning()
            super.onTaskRemoved(rootIntent)
        }

        private fun ensureServiceStaysRunning() {
            val restartAlarmInterval = 60 * 1000
            val resetAlarmTimer = 30 * 1000L
            // From this broadcast I am restarting the service
            val restartIntent = Intent(this, ServiceRestartBroadcast::class.Java)
            restartIntent.action = "RestartedViaAlarm"
            restartIntent.flags = Intent.FLAG_RECEIVER_FOREGROUND
            val alarmMgr = getSystemService(Context.ALARM_SERVICE) as AlarmManager
            val restartServiceHandler = @SuppressLint("HandlerLeak")
            object : Handler() {
                override fun handleMessage(msg: Message) {
                    val pendingIntent = PendingIntent.getBroadcast(applicationContext, 87, restartIntent, PendingIntent.FLAG_CANCEL_CURRENT)
                    val timer = System.currentTimeMillis() + restartAlarmInterval
                    val sdkInt = Build.VERSION.SDK_INT
                    if (sdkInt < Build.VERSION_CODES.KitKat)
                        alarmMgr.set(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                    else if (Build.VERSION_CODES.KitKat <= sdkInt && sdkInt < Build.VERSION_CODES.M)
                        alarmMgr.setExact(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                    else if (sdkInt >= Build.VERSION_CODES.M) {
                        alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                    }
                    sendEmptyMessageDelayed(0, resetAlarmTimer)
                    stopSelf()
                }
            }
            restartServiceHandler.sendEmptyMessageDelayed(0, 0)
        }

    }

Veuillez partager votre suggestion si vous avez rencontré le même type de problème et avez réussi à trouver une solution à ce problème.

6
Sagar Trehan

J'ai analysé le problème sur OnePlus, car je suis dans la même situation que vous. Comme je le vois actuellement, il n'y a pas de solution. OnePlus suit clairement une mauvaise pratique.

Comme ils n’ont pas publié le code source qui tue les processus de cette manière, j’ai téléchargé un OnePlus ROM (j’ai choisi OnePlus 5T 5.1.5), l’extrais, je trouve le .class qui le fait (OnePlusHighPowerDetector.class services.vdex), décompilez-le et essayez de savoir ce qui se passe.

Vous pouvez trouver une version de cette classe ici (ce n'est pas de moi, et peut-être que ce n'est pas la même version que celle que j'ai utilisée): https://github.com/joshuous/oneplus_blobs_decompiled/blob/master/com /Android/server/am/OnePlusHighPowerDetector.Java

Malheureusement, les fonctions les plus importantes ne sont pas décompilées correctement. Mais on peut quand même analyser le bytecode. Voici ce que j'ai trouvé:

  • OnePlus tue les processus en arrière-plan (il semble que peu importe qu'il ait ou non un service de premier plan) sans pitié. La quasi-totalité d'entre eux. Peu importe le peu de ressources CPU utilisées. Mon application utilise moins de 1% et elle est tuée.
  • Si l'application est épinglée dans la liste des applications récentes (déterminée par une liste de com_oneplus_systemui_recent_task_lockd_list), elle n'est pas supprimée. Ainsi, l'utilisateur peut enregistrer l'application en l'épinglant. Mais c'est gênant pour eux.
  • Oneplus fournit une liste de processus, qu'ils ne tuent pas. Cette liste est dans oneplus-framework-res.apk/res/values/array.xml, avec la clé string-array name="backgroundprocess_detection_app_whitelist". Cette liste contient principalement des applications de carte et de fitness.1.
  • Peut-être existe-t-il d'autres facteurs susceptibles de sauver un processus d'être tué, je n'ai pas analysé davantage le problème. Si vous avez le temps, consultez OnePlusHighPowerDetector.Java.

Voici les étapes nécessaires pour décompiler un fichier .vdex:

  • utilisez Vdex Extractor pour créer un .dex à partir de .vdex (vous devrez peut-être utiliser l'option --ignore-crc-error)
  • utilisez JADX pour décompiler un fichier .dex (c'est actuellement le meilleur décompilateur à mon avis), ou utilisez dex2jar pour créer un fichier .jar à partir de .dex, et utilisez n'importe quel décompilateur pour décompiler le fichier .jar.

1Rant: cela montre à quel point la situation actuelle est mauvaise. OnePlus, est-ce vraiment la solution? Vous avez choisi 10-15 applications, qui peuvent être exécutées en arrière-plan, et vous ne vous souciez pas de toutes les autres? Comment créer une nouvelle application de fitness/carte/lecteur de musique qui fonctionne en toute sécurité sur votre appareil?

9
geza

utilisez la méthode startForeground(id, notification) dans votre service.

L'utilisateur doit savoir que quelque chose qui ne peut pas être tué par le système fonctionne en arrière-plan. Le système Android doit donc en informer l'utilisateur par une notification. Il sait donc exactement ce qui tue sa batterie.

En passant, vous pouvez utiliser WakeLock .

 PowerManager powerManager = (PowerManager)getSystemService(Context.POWER_SERVICE);
 PowerManager.WakeLock wakeLock = powerManager.newWakeLock(PARTIAL_WAKE_LOCK, 
 TAG_FOR_DEBUG);
 wakeLock.acquire()

Mais lisez à propos de WakeLock avant de l’utiliser ..__ Et n'oubliez pas de le désactiver lorsque votre travail est terminé.

Vous pouvez vérifier les verrous de sillage activés actuels à l’aide de la commande adb Shell dumpsys power dans Terminal

1
Viktor Vasyliev

J'ai passé plusieurs heures à résoudre ce problème. Xiaomi téléphone, Oreo. J'ai la solution, 2 étapes:

  1. Xiaomi a besoin d'une autorisation supplémentaire: Paramètres-> Autorisations-> Démarrage automatique. Il existe une liste d'applications pouvant être démarrées automatiquement. BOOT_COMPLETED et START_STICKY ne suffisent pas. Facebook, Outlook, Skype + mon application est répertoriée ici. Je ne sais pas comment demander à l'utilisateur par programme d'accorder cette autorisation. 

  2. Le service est recréé mais NON redémarré, même si vous renvoyez START_STICKY. Après avoir supprimé l'application (supprimez les applications en cours d'exécution), onCreate est appelé à nouveau, mais PAS onStartCommand. Essayez Log.d () pour le voir. J'ai déplacé toute la logique dans onCreate EXCEPT en créant la notification, en appelant startForeground () et en renvoyant START_STICKY. Cela doit rester dans onStartCommand ().

L'optimisation de Batery doit également être désactivée pour l'application, mais elle était déjà requise par la version précédente d'Android.

Maintenant, mon service continue à fonctionner ou est recréé avec 100% de succès en quelques secondes.

1
Milan Švec

J'ai rencontré le même problème dans mon OnePlus3 pour mon application. Ensuite, j'ai remarqué ces lignes dans logcat, chaque fois que mon application et mon service d'avant-plan étaient tués:

10-12 18:30:40.644  1439  1519 I OHPD    : [BgDetect]force stop com.mycompany.MyAndroidApp (uid 10905) level 0
10-12 18:30:40.645  1439  1519 I ActivityManager: Force stopping com.mycompany.MyAndroidApp appid=10905 user=0: from pid 1439
10-12 18:30:40.645  1439  1519 I ActivityManager: Killing 23139:com.mycompany.MyAndroidApp/u0a905 (adj 200): stop com.mycompany.MyAndroidApp
10-12 18:30:40.647  1439  1519 W ActivityManager: Scheduling restart of crashed service com.mycompany.MyAndroidApp/md55852635cfc9dced970aa39ae09e74ead.MyForegroundService in 1000ms
10-12 18:30:40.651  1439  1519 I ActivityManager:   Force finishing activity ActivityRecord{a5c8d39 u0 com.mycompany.MyAndroidApp/md55852635cfc9dced970aa39ae09e74ead.MainActivity t3108}
10-12 18:30:40.655  1439  1519 I ActivityManager:   Force stopping service ServiceRecord{6052f94 u0 com.mycompany.MyAndroidApp/md55852635cfc9dced970aa39ae09e74ead.MyForegroundService}
10-12 18:30:40.660  1439  1519 I OHPD    : [BgDetect]chkExcessCpu level: 1 doKills: true critical false uptime: 300328
10-12 18:30:40.710  1439  8213 I WindowManager: WIN DEATH: Window{c381342 u0 com.mycompany.MyAndroidApp/md55852635cfc9dced970aa39ae09e74ead.MainActivity}

Puis, recherchant sur Internet les mots-clés 'OHPD: [BgDetect] chkExcessCpu niveau: 1 doKills: true' m'a conduit à cette SO réponse:

Empêche le service en arrière-plan d'être tué en raison de la "détection d'un nombre excessif de processeurs sur un processus" }

Comme suggéré dans cette réponse et le problème de GitHub, verrouiller/épingler mon application a empêché Oxygen OS de tuer mon application (MainActivity and Forground Service).

0
Jagan