22
votes

L'appareil Huawei tue mon service de premier plan, même avec la solution dontkillmyapp.com

Je développe une application qui est essentiellement un logiciel de suivi de localisation . Lorsque vous le démarrez, il enregistre les emplacements et les envoie à un serveur.

Le code fonctionne depuis 5 ans maintenant sans aucune modification, sans aucune erreur.

Il est implémenté avec un simple service de premier plan.

Au cours des derniers mois, des erreurs ont été signalées par les utilisateurs concernant les arrêts de service au hasard sur les appareils Huawei. J'ai d'abord pensé que c'était une sorte de crash rare / nouveau sur les androïdes plus récents, mais il n'y avait aucun journal d'erreurs dans Fabric.

Je l'ai essayé dans un nouvel appareil Huawei et pour ma plus grande surprise, ce phénomène existe vraiment. Les appareils Huawei (avec EMUI) détruisent vraiment les services de premier plan après quelques minutes.

C'est vraiment vraiment mauvais pour mon application, tout d'abord, les utilisateurs veulent exécuter cette application de suivi pendant de longues heures, et deuxièmement, les derniers mois ont fait de Huawei un choix populaire parmi les utilisateurs d'Android. Comme 10% de ma base d'utilisateurs a un appareil Huawei.

Je connais https://dontkillmyapp.com/ C'est un excellent site Web pour obtenir des informations sur ce problème.

J'ai essayé leur solution - qui consiste essentiellement à ajouter un wakelock avec une balise spécifique à mon service, de sorte que l'EMUI de Huawei ne le tuera pas.

J'ai essayé cela de la manière suivante, mais mon appareil de test Huawei tue toujours mon service de premier plan après quelques minutes.

Code à l'intérieur de mon service:

J'acquiert essentiellement un wakelock dans le rappel onCreate du service.

 private void acquireLock() {

    if (wakeLock == null) {
        PowerManager mgr = (PowerManager) getSystemService(Context.POWER_SERVICE);
        if (mgr != null) {

            if (Build.MANUFACTURER.toLowerCase().equals("huawei")) {
                lockTag = "LocationManagerService";
            }

            wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, lockTag);

            Log.i("MY_TAG", "tag:" + lockTag);
        }
    }
    if (wakeLock != null && !wakeLock.isHeld()) {
        wakeLock.acquire();
        //also tried with: wakeLock.acquire(1000*60*60*72); 3 days wakelock just in case.
        Log.i("MY_TAG", "wakeLock acquired!");
    }
}

@Override
public void onCreate() {
    acquireLock();
}

ÉDITER:

Clraification: Mon service est un service de premier plan, avec une notification permanente. Il peut bien fonctionner pendant DAYS sur d'autres appareils.

S'il vous plait aidez si vous le pouvez,

Adam


5 commentaires

Huawei personnalise AOSP et met plus de restrictions dans l'optimisation de la batterie et à d'autres endroits afin que l'utilisateur ne rencontre pas un problème tel que


avez-vous résolu le problème? Je travaille là-dessus si vous en avez besoin, je peux vous donner de bonnes suggestions.


@ Mr.AF adorerait les entendre


Je travaille également avec foregroundService, avec le système d'exploitation Android AOSP, l'action requise est: afficher la notification au premier plan et désactiver la somnolence par l'utilisateur. Avec les appareils avec ROM OEM et l'optimisation de la batterie OEM, vous devez suivre dontkillmyapp. Dontkillmyapp classe Huawei comme le pire fabricant et affirme qu'il n'y a pas de solution, à la fois côté utilisateur et côté développeur.


Avez-vous trouvé une solution? J'ai déjà essayé awakelock, cela aide un peu mais ne garantit pas de maintenir le service en vie. La seule chose qui aide vraiment est dans les paramètres de la batterie pour que mon application passe de l'optimisation automatique à l'optimisation manuelle.Cependant, ce n'est pas une bonne solution pour moi car cela dépend de l'utilisateur qui peut être paresseux.


9 Réponses :


0
votes

Ce n'est pas lié aux téléphones Huawei mais à la version Android OS .

Depuis la sortie d' Android 26 (Oreo). Google a décidé de définir certaines restrictions et limitations et certaines autorisations ne seraient pas accordées non plus. En gros, je voudrais vous suggérer d'en savoir plus sur Google Oreo et plus tard sur la sécurité et la politique des autorisations.

J'ai votre problème et mon application fonctionne parfaitement sous < Android 26 mais pour la version ultérieure, j'ai rencontré un gros désastre et je travaille dessus pour le gérer pour les nouveaux utilisateurs de la version Android en ce moment. Par conséquent, vous devez vous adapter à la politique actuelle de Google. Sinon, vous devez prendre la décision SHIFT + DELETE .


Clraification: Mon service est un service de premier plan, avec une notification permanente. Il peut bien fonctionner pendant DAYS sur d'autres appareils.

Comme vous l'avez précisé, le cycle de vie du service de votre application se poursuit lorsque l'application est fermée. Cependant, selon les limites d'emplacement d'arrière-plan

Dans le but de réduire la consommation d'énergie, Android 8.0 (niveau d'API 26) limite la fréquence à laquelle les applications d'arrière-plan peuvent récupérer l'emplacement actuel de l'utilisateur. Les applications ne peuvent recevoir des mises à jour de localisation que quelques fois par heure.

En conséquence, votre service de premier plan peut être fermé en raison de mises à jour de localisation supplémentaires qui sont interdites.

Comment réparer

Selon les limites d'emplacement d'arrière-plan

Si une application est au premier plan sur un appareil exécutant Android 8.0 (niveau d'API 26), le comportement de mise à jour de l'emplacement est le même que sur Android 7.1.1 (niveau d'API 25) et versions antérieures.

Par conséquent, si l'utilisateur ferme l'application, même si le service de premier plan a été démarré intentionnellement ou non, notifiez l'utilisateur pour qu'il rouvre l'application .

Ajuster le comportement de localisation de votre application

  • Mettez votre application au premier plan.

  • Démarrez un service de premier plan dans votre application en appelant startForegroundService (). Lorsqu'un tel service de premier plan est actif,
    il apparaît comme une notification en cours dans la zone de notification.

  • Utilisez des éléments de l'API Geofencing, tels que GeofencingClient, qui sont optimisés pour minimiser la consommation d'énergie.

  • Utilisez un écouteur de localisation passif, qui peut recevoir des mises à jour de localisation plus rapides si des applications de premier plan demandent des mises à jour de localisation à un rythme plus rapide.


14 commentaires

Pouvez-vous citer de telles restrictions? Y en a-t-il un qui pourrait rendre cela possible?


@AdamVarhegyi je ne peux pas dire exactement quel est votre problème parce que quelque chose que vous montrez en question n'est pas utile, mais je peux vous donner l'exemple.


C'est simple, mon service de premier plan est tué par OS dans Huaweis. (EMUI) C'est tout.


@AdamVarhegyi votre service de premier plan doit-il continuer pendant que l'application est fermée? ou cela fonctionne pendant le cycle de vie de l'application?


@AdamVarhegyi j'ai édité le post.J'espère que ça marche pour toi.cuz ça a marché pour moi.Bonne chance


Je pense que cette réponse n'est pas vraie et ne devrait pas être acceptée comme solution. Le fait est que les services de premier plan continuent de fonctionner sur tous les appareils d'implémentation Android modèles conformément au mode somnolent. Essentiellement, si vous déménagez (changement d'emplacement), vous déplacez l'appareil (changements de capteurs) ou une notification de haute priorité arrive, l'appareil maintient toujours le service en cours d'exécution. Dans mon cas, mon application fonctionne sur tous les appareils testés à l'exception de Huawei.


OK alors vous avez peut-être raison, je n'ai pas réalisé que vous vouliez dire cela.


Cette réponse est absolument fausse ... cela est causé par le problème de l'économie d'énergie des téléphones mobiles, la plupart des téléphones mobiles chinois ont ce problème Huawei est l'un d'entre eux


@lazy, je pense que vous vous trompez absolument car cette réponse est presque un document officiel et il n'y a aucune différence entre Huawei ou Sumsung ou LG. à moins que Google ne mette un mauvais document.


@ Mr.AF Le document officiel n'est bien sûr pas un problème, mais il n'a rien à voir avec le problème de l'opération, La cause de ce problème est que le système d'exploitation dans l'optimisation de la batterie OEM de Huawei tue le service de premier plan


@lazy, je vous recommande d'en savoir plus sur la conception du système d'exploitation et du système, la limitation de l'utilisation de la batterie et la politique de confidentialité de Google, alors vous comprendrez ce que j'ai expliqué.


Qu'entendez-vous par Notify the user to open the app again. ?


@IgorGanapolsky lorsque le service de premier plan est en ligne mais que l'application est fermée.


Je pense que le mode Doze sous Android 11 est plus intelligent que tout cela.



-2
votes

Je pense que le problème réside dans la manière dont vous gérez les services dans votre application.
Le principal est que vous devez maintenir une notification en cours d'exécution dans la barre d'état qui indique que le service lié à votre application est en cours d'exécution et que l'utilisateur final peut mettre fin au service en cliquant sur la notification.
C'est la combinaison du service + notification + politiques requises.
J'espère que cela aide.


1 commentaires

Quelles sont les politiques requises ?



-2
votes

J'ai testé le code ci-dessous dans un huawei et cela a fonctionné. Peut-être que vous pouvez l'utiliser dans votre service avec notification. Ce n'est peut-être pas parfait même si je suis encore novice sur le sujet.

public class SensorService extends Service {
public int counter=0;
Context context;



public SensorService(Context applicationContext) {
    super();


    Log.i("HERE", "here I am!");
}

static PhoneCallListener phoneListener = null;
TelephonyManager telephonyManager;
public SensorService() {


}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    super.onStartCommand(intent, flags, startId);

    String CHANNEL_ID = "yourid";
    String CHANNEL_NAME = "yourchannel";
    NotificationChannel channel = null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        channel = new NotificationChannel(CHANNEL_ID,
                CHANNEL_NAME, NotificationManager.IMPORTANCE_DEFAULT);
    }

    NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        if (manager != null) {
            manager.createNotificationChannel(channel);
        }
    }



    Notification notification = null;
    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
        notification = new Notification.Builder(this, CHANNEL_ID)
                .setContentTitle("title")
                .setContentText("text")

                .setAutoCancel(true)
                .build();
        this.startForeground(1,notification);
    }






    telephonyManager = (TelephonyManager) this
            .getSystemService(Context.TELEPHONY_SERVICE);



    if(phoneListener == null) {

        if (telephonyManager != null) {
            phoneListener = new PhoneCallListener();
            telephonyManager.listen(phoneListener,
                    PhoneStateListener.LISTEN_CALL_STATE);
        }
    }

    System.out.println("SERVICE");
    return START_STICKY;


}


@Override
public void onDestroy() {
    super.onDestroy();
    Log.i("EXIT", "ondestroy!");
    Intent broadcastIntent = new Intent(this, SensorRestarterBroadcastReceiver.class);

    sendBroadcast(broadcastIntent);




    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        context.startForegroundService(new Intent(context, SensorService.class));

    } else {
        context.startService(new Intent(context, SensorService.class));
    }


    Intent brcast = new Intent(this, AlarmReceiver.class);

    sendBroadcast(brcast);


}



@Nullable
@Override
public IBinder onBind(Intent intent) {
    context = getApplicationContext();
    return null;
}


}


4 commentaires

Forcer le redémarrage sur onDestroy? C'est très dangereux je pense. C'est comme attacher un autre moteur à la pédale de frein en veillant à ce que le véhicule ne s'arrête pas du tout.


Précisément. Mais si vous souhaitez arrêter le service, il peut toujours être tué par "Forcer l'arrêt" dans le menu des paramètres.


Qu'est-ce que TELEPHONY_SERVICE a à voir avec la question d'OP?


Cela ressemble plus à un hack qu'à une solution réelle.



3
votes

J'ai eu un problème similaire il y a quelques mois, j'ai passé beaucoup de temps à chercher une solution et finalement j'ai trouvé ça (je ne sais pas si ça marche pour vous, mais ça m'a aidé).

J'ai implémenté dans une application que j'ai développée, un service qui récupère la position de l'utilisateur toutes les minutes, cette position est enregistrée dans la base de données sqlite de l'appareil. J'ai besoin que l'application fonctionne toujours sans interruption.

Dans la phase de test, j'ai constaté que certains appareils avaient interrompu l'exécution de mon code (comme dans votre cas).

Après plusieurs coups de poing sur mon bureau, je me suis rendu compte que le problème était lié aux options d'économie d'énergie qui sont différentes pour certains fabricants mais aussi pour les versions Android.

Cette solution m'aide à:

android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS  

En gros, je demande à l'utilisateur (je ne peux pas le faire autrement) d'autoriser mon application à éviter d'être optimisée (ce code fonctionne pour Build.VERSION.SDK_INT> = 23)

Le fichier manifeste a besoin de cette autorisation:

@SuppressLint({"NewApi", "BatteryLife"})
private void checkOptimization() {
    String packageName = getApplicationContext().getPackageName();
    PowerManager pm = (PowerManager) getApplicationContext().getSystemService(Context.POWER_SERVICE);
    if (pm != null) {
        if (!pm.isIgnoringBatteryOptimizations(packageName)) {
                Intent intent = new Intent();
                intent.setAction(ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
                intent.setData(Uri.parse("package:" + ctx.getPackageName()));
                ctx.startActivity(intent);
        } else {
            new initialize().execute();
        }
    }
}


1 commentaires

Cela n'empêche pas complètement l'application d'être tuée.



-1
votes

Essayez de passer par ici pour une fois - explication n codez un service d'arrière-plan sans fin en utilisant un récepteur de diffusion en définissant sa priorité sur HIGH

Je l'utilise et cela fonctionne parfaitement pour moi jusqu'à présent, nous n'avons aucun client utilisant des appareils Huawei, alors IDK comment cela fonctionnera-t-il sur ces appareils, mais comme cela fonctionne bien sur Samsung pour nous, je suppose que cela fonctionnera fonctionne également comme prévu sur Huawei. Donnez-lui un coup d'oeil.


0 commentaires

3
votes

Ce n'est pas une solution liée à Huawei, mais il existe des actions utiles pour atténuer le problème.

L'appel de startForeground et START_STICKY est requis dans ce cas

/** YourActivity **/

private val mConnection = object : ServiceConnection {

    override fun onServiceConnected(className: ComponentName, iBinder: IBinder) {
        // The system calls this to deliver the IBinder returned by the service's onBind() method.
        val binder = iBinder as YourService.YourBinder
        service = binder.getService()
        bound = true
    }

    override fun onServiceDisconnected(arg0: ComponentName) {
        // The Android system calls this when the connection to the service is unexpectedly lost, such as when the service has crashed or has been killed. This is not called when the client unbinds
        bound = false
    }
}

private fun bindYourService() {
    Intent(this, YourService::class.java).also { intent ->
        applicationContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
    }
}

private fun unbindYourService() {
    try {
        applicationContext.unbindService(mConnection)
    } catch (e: Exception) {
        Timber.e(e)
    }
    bound = false
}

/** YourService **/

private val binder = YourBinder()

inner class YourBinder: Binder() {
    fun getService(): YourService = this@YourService
}

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

override fun onRebind(intent: Intent?) {
    super.onRebind(intent)
}

override fun onUnbind(intent: Intent?): Boolean {
    return super.onUnbind(intent)
}

Ces deux méthodes permettent à l'utilisateur de désactiver la somnolence (Oreo>) et d'activer l'autorisation de démarrage automatique (certains OEM) pour préserver le cycle de vie du service STICKY.

/** YourService **/

private val wakeLock: PowerManager.WakeLock by lazy {
    (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
        newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "ServiceWakelock")
    }
}

private fun acquireWakelock() {
    try {
        wakeLock.let {
            wakeLock.setReferenceCounted(false)
            if (!wakeLock.isHeld) {
                wakeLock.acquire()
            }
        }
    } catch (e: RuntimeException) {
    }
}

private fun releaseWakelock() {
    try {
        wakeLock.let {
            if (it.isHeld) {
                it.release()
            }
        }
    } catch (e: RuntimeException) {
    }
}

override fun onCreate() {
    super.onCreate()
    acquireWakelock()
}

override fun onDestroy() {
    releaseWakelock()
    super.onDestroy()
}

L'utilisation du verrouillage de réveil partiel aide également à atténuer mais ne garantit pas de maintenir le service en vie.

/** YourActivity **/

fun openBatteryOptimization(context: Context) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        val intent = Intent()
        intent.action = Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS
        context.startActivity(intent)
    } else {
        //Timber.d("Battery optimization not necessary")
    }
}

fun openAutostartSettings(context: Context) {
    try {
        val intent = Intent()
        val manufacturer = Build.MANUFACTURER
        if ("xiaomi".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.miui.securitycenter",
                "com.miui.permcenter.autostart.AutoStartManagementActivity"
            )
        } else if ("oppo".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.coloros.safecenter",
                "com.coloros.safecenter.permission.startup.StartupAppListActivity"
            ) //need "oppo.permission.OPPO_COMPONENT_SAFE" in the manifest
        } else if ("vivo".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.vivo.permissionmanager",
                "com.vivo.permissionmanager.activity.BgStartUpManagerActivity"
            )
        } else if ("Letv".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.letv.android.letvsafe",
                "com.letv.android.letvsafe.AutobootManageActivity"
            )
        } else if ("Honor".equals(manufacturer, ignoreCase = true)) {
            intent.component = ComponentName(
                "com.huawei.systemmanager",
                "com.huawei.systemmanager.optimize.process.ProtectActivity"
            )
        } else {
            //Timber.d("Auto-start permission not necessary")
        }
        val list = context.packageManager
            .queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
        if (list.size > 0) {
            context.startActivity(intent)
        }
    } catch (e: Exception) {
    }
}

Avec Binder, vous pouvez vérifier si le service est en cours d'exécution ou non (et le redémarrer) et vous pouvez obtenir l'instance du service.

/** YourService **/

override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
    startForeground(
        App.NOTIFICATION_ID_YOUR_SERVICE,
        buildNotification("Foo bar")
    )
    return START_STICKY
}


5 commentaires

J'ai lu votre réponse. de bons matériaux, même s'ils ne sont peut-être pas une réponse complète, mais aident à obtenir une réponse complète.


Qu'est-ce que AutoStartManagementActivity ?


@IgorGanapolsky Paramètres de démarrage automatique de Xiaomi


Bonne réponse, mais c'est le problème de Huawei. Parce que, par exemple, mon application fonctionne bien avec Samsung, et Huawei la tue après l'écran de verrouillage. Lorsque j'ai supprimé l'application de l'optimisation de la batterie dans les paramètres, l'application fonctionne correctement même après l'écran de verrouillage, mais après un certain temps, elle est toujours tuée par le système d'exploitation. J'utilise ForegroundService, sticky, avec WakeLock dedans.


n'importe qui peut fournir la réponse complète en java?



0
votes

L'EMUI de Huawei surveille et gère strictement la consommation de la batterie des applications. Même l'application a les autorisations de lancement secondaire, d'exécution en arrière-plan et de lancement automatique, EMUI peut la tuer ou ses processus d'arrière-plan après un certain temps.

EMUI gère cela via une liste et comme je sais si une application rencontre de tels problèmes, il est nécessaire de demander une autorisation spéciale pour figurer sur cette liste.


1 commentaires

"il est nécessaire de demander une autorisation spéciale pour figurer sur cette liste.". Dites-m'en plus sur ces autorisations spéciales.



0
votes

Pour moi, ce qui suit a aidé (changements dans les paramètres de l'appareil Huawei):

Étape 1. Configurez votre smartphone Huawei pour permettre à des applications spécifiques de s'exécuter en arrière-plan

Étape 2. Désactivez l'optimisation de la batterie pour les mêmes applications

Sources:

https://www.digitalcitizen.life/stop-huawei-from-closing-apps-when-you-lock-screen

https://dontkillmyapp.com/


0 commentaires

0
votes

C'est comme un jeu entre les développeurs d'applications qui souhaitent maintenir les applications en vie et les développeurs de systèmes d'exploitation qui souhaitent détecter et tuer les processus inutiles pour optimiser les performances du système d'exploitation.

Pour les appareils Huawei, il existe 3 façons élégantes de garder l'application en vie, comme je l'ai essayé, et elles ont des avantages et des inconvénients.

1. Guider les utilisateurs à donner à l'application les autorisations de s'exécuter en arrière-plan.

Vous pouvez implémenter le code pour demander aux utilisateurs d'accorder les autorisations nécessaires dans Paramètres-> Applications-> Lancement d'application

    private void showActivity(@NonNull String packageName, @NonNull String activityDir) {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(packageName, activityDir));
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }

    private void goSettings() {
        try {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
        } catch (Exception e) {
            showActivity("com.huawei.systemmanager",
                    "com.huawei.systemmanager.optimize.bootstart.BootStartActivity");
        }
    }

Cela peut être fait directement dans le code, mais les utilisateurs doivent effectuer les réglages et dans certains scénarios (optimiser manuellement le système d'exploitation), l'application peut encore être tuée.

2. Intégrez le HMS Push Kit de Huawei.

Il fournit des messages de données de haute priorité, qui peuvent vous permettre d'envoyer des messages de données aux appareils de destination et lorsque les messages arrivent, l'application correspondante est directement démarrée. De cette façon, vous pouvez activer l'application en envoyant des messages périodiquement ou déclenchés par certaines activités, mais l'envoi de messages de données hautement prioritaires nécessite une demande d'autorisation et cela prend plusieurs semaines.

3. Demandez l'autorisation d'activité en arrière-plan EMUI.

C'est probablement la solution une fois pour toutes, elle donne à l'application la permission de fonctionner en arrière-plan et d'ignorer la consommation d'énergie et l'optimisation du système d'exploitation. Pas besoin de changer le code et très simple, mais l'application a besoin d'environ un mois pour être examinée et il y a une chance qu'elle soit refusée.


0 commentaires