Comment gérer le code lorsque l'application est tuée en glissant dans Android?

104

Si mon application est en cours d'exécution et que j'appuie sur le bouton d'accueil, l'application passe en arrière-plan. Maintenant , si un appui long sur le bouton d'accueil et de tuer l'application en faisant glisser de la liste récente de l' application, aucun des événements comme onPause(), onStop()ou onDestroy()s'appelle plutôt le processus est terminé. Donc, si je veux que mes services s'arrêtent, suppriment les notifications et désenregistrent les écouteurs, comment puis-je faire cela? J'ai lu pas mal d'articles et de blogs, mais je n'ai pas obtenu d'informations utiles et je n'ai trouvé aucune documentation à ce sujet. Toute aide serait appréciée. Merci d'avance.

CodeWarrior
la source
Je résous mon problème. Check
MysticMagicϡ
@MysticMagic Cela fonctionne pour un service, qu'en est-il de l'annulation des notifications et de la désinscription des écouteurs?
CodeWarrior
Vous pouvez désinscrire des écouteurs dans onTaskRemoved. Tu ne peux pas? Et même pour l'annulation des notifications
MysticMagicϡ
@ MysticMagicϡ Que faire si je veux faire la même chose lors de la mise à jour de l'application?
reji

Réponses:

150

Je viens de résoudre un problème similaire.

Voici ce que vous pouvez faire s'il s'agit simplement d'arrêter le service lorsque l'application est tuée en faisant glisser la liste des applications récentes.

Dans votre fichier Manifest, gardez le drapeau stopWithTaskcomme truepour le service. Comme:

<service
    android:name="com.myapp.MyService"
    android:stopWithTask="true" />

Mais comme vous dites que vous voulez désinscrire les auditeurs et arrêter la notification, etc., je suggérerais cette approche:

  1. Dans votre fichier Manifest, gardez le drapeau stopWithTaskcomme falsepour le service. Comme:

    <service
        android:name="com.myapp.MyService"
        android:stopWithTask="false" />
  2. Maintenant dans votre MyServiceservice, remplacez la méthode onTaskRemoved. (Ceci ne sera déclenché que si stopWithTaskest défini sur false).

    public void onTaskRemoved(Intent rootIntent) {
    
        //unregister listeners
        //do any other cleanup if required
    
        //stop service
        stopSelf();  
    }

Référez - vous à ma question pour plus de détails, qui contient également une autre partie du code.

J'espère que cela t'aides.

MysticMagicϡ
la source
Merci. Penser si cette méthode pourrait être utilisée pour identifier les attaques par glissement. Service vierge simple?
Kiran
Oui @Kiran, vous pouvez essayer. :)
MysticMagicϡ
1
@ MysticMagicϡ salut, une question, j'ai essayé d'utiliser votre solution, mais il semble que de longues opérations comme l'envoi de données au serveur, les analyses vers Google, etc. échouent parfois. Se pourrait-il que le processus soit tué avant que cette méthode n'ait une chance de se terminer complètement? Avez-vous également essayé cette méthode sur un appareil Huawei? Il semble que onTaskRemoved ne soit jamais appelé sur ces appareils. Des idées pourquoi?
Alon Minski
2
@ MysticMagicϡ public void onTaskRemoved (..) La méthode n'appelle pas dans Android O. Fonctionne bien dans d'autres systèmes d'exploitation mais rencontre un problème dans Android O.
Jay Patel
2
Ce code ne fonctionne pas pour Android O en raison de la limitation du service en arrière-plan. Existe-t-il un moyen de gérer cela sur tous les appareils Android?
Aanal Mehta le
33

J'ai trouvé un moyen de le faire

faire un service comme celui-ci

public class OnClearFromRecentService extends Service {

@Override
public IBinder onBind(Intent intent) {
    return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    Log.d("ClearFromRecentService", "Service Started");
    return START_NOT_STICKY;
}

@Override
public void onDestroy() {
    super.onDestroy();
    Log.d("ClearFromRecentService", "Service Destroyed");
}

@Override
public void onTaskRemoved(Intent rootIntent) {
    Log.e("ClearFromRecentService", "END");
    //Code here
    stopSelf();
}

}

2) Enregistrez ce service dans manifest.xml

<service android:name="com.example.OnClearFromRecentService" android:stopWithTask="false" />

3) Ensuite, démarrez ce service sur votre activité de démarrage

startService(new Intent(getBaseContext(), OnClearFromRecentService.class));

Et maintenant, chaque fois que vous effacerez votre application d'Android récent, cette méthode onTaskRemoved () s'exécutera.

emilpmp
la source
1
Copiez et collez un à un à partir de cette réponse: stackoverflow.com/questions/21040339
Flot2011
17

J'ai résolu un problème similaire. Si vous voulez après avoir glissé de la tâche récente et au prochain lancement, il se comporte correctement, suivez les étapes ci-dessous: -

1) Enregistrez l'ID de processus dans la préférence partagée:

SharedPreferencesUtils.getInstance().putInt(SharedPreferencesUtils.APP_PROCESS_ID, android.os.Process.myPid());

2) Lorsque l'application est lancée à partir du lanceur après avoir été effacée de la tâche récente, procédez comme suit:

int previousProcessID = mSharedPreferencesUtils.getInt(SharedPreferencesUtils.APP_PROCESS_ID);

int currentProcessID = android.os.Process.myPid();

if ((previousProcessID == currentProcessID)) {
    // This ensures application not killed yet either by clearing recent or anyway
} else {
    // This ensures application killed either by clearing recent or by anyother means
}
Anshul Agarwal
la source
Confirmé - utilisé sur Android 8 sans service.onTaskRemoved () - bien fait
Gal Rom
4
Cette solution ne détecte que si l'application a été redémarrée, mais vous ne pouvez pas détecter le moment exact où l'application a été tuée
Vadim Kotov
6

Lorsque vous appuyez sur Accueil - onPauseet que onStopvotre activité est appelée, vous devez donc faire toutes les économies et le nettoyage pour le moment, car la plate-forme Android ne garantit pas davantage que onDestroyou toute autre méthode de cycle de vie serait invoquée, de sorte que le processus pourrait être tué sans aucune notification.

désert
la source
12
Merci pour la suggestion, mais je ne veux pas tuer mes notifications, services et écouteurs, onPause()et onstop()ils ne doivent être tués que lorsque l'utilisateur quitte l'application, c'est-à-dire se déconnecter.
CodeWarrior
1

Vous devez enregistrer vos données lors de l' onPause()appel de. Regardez ce diagramme du cycle de vie: Développeur Android

Vous pouvez voir qu'une application peut être tuée après onPause()ou onStop().

Manipulez vos données là-bas et récupérez-les dans onRestart()\ onCreate().

bonne chance!

Fashizel
la source
1

Vous ne pouvez pas gérer le balayage, car le système supprime simplement votre processus de la mémoire sans appeler aucun rappel.

J'ai vérifié qu'avant que l'utilisateur appelle l'écran "Applications récentes", onPause () sera toujours appelé. Vous devez donc enregistrer toutes les données dans la méthode onPause sans vérifier isFinishing ().

Pour vérifier le bouton de retour, utilisez la méthode onBackPressed.

Ahmad
la source
1
Je pense que c'est la seule méthode sûre pour toutes les versions d'Android.
Sergio
@Sergio oui c'est la méthode la plus sûre et je l'ai testée sur une application à plus grande échelle.
Ahmad
1

ViewModel.onCleared () peut être utile, si le but est de libérer une ressource (peut-être un système fonctionnant ailleurs sur le réseau) lorsque l'utilisateur exécute une sortie surprise en balayant, plutôt qu'en appuyant sur le bouton "stop" ou sur le bouton. [C'est ainsi que je suis arrivé à cette question à l'origine].

L'application ne reçoit pas de notification et Activity.onDestroy () est appelé pour les changements de configuration tels que les changements d'orientation, donc la réponse n'est pas là. Mais ViewModel.onCleared est appelé lorsque l'application est balayée (ainsi que lorsque l'utilisateur se retire de l'activité). Si la ressource que vous souhaitez utiliser est associée à plusieurs activités dans la pile, vous pouvez ajouter des nombres de références ou un autre mécanisme pour décider si ViewModel.onClear doit libérer la ressource.

C'est encore une autre des nombreuses bonnes raisons d'utiliser ViewModel.

- Bob

Bob Cram
la source
1

Je ne sais pas vraiment pourquoi les approches ci - dessus ne fonctionnent pas sur mon cas même je jeu android:stopWithTask="false"que onTaskRemoved()pas appelé.

Une autre bonne approche consisterait à utiliser AndroidViewModel. Celui-ci fonctionne même sur le cas où l'utilisateur quitte l'application en appuyant sur le bouton retour.

Juste lié la ViewModelclasse à votre MainActivitypuis faites votre onCleared()rappel de tâche .

Exemple:

public class MainViewModel extends AndroidViewModel {
    public MainViewModel(@NonNull Application application) {
        super(application);
    }

    @Override
    protected void onCleared() {
        // Do your task here
        Log.e("MainViewModel", "OnCleared mainViewModel");
        super.onCleared();
    }
}

puis liez-le à votre MainActivity:

MainViewModel viewModel = new ViewModelProvider(this).get(MainViewModel.class);

~ Voila!

droidhs
la source
il ne fonctionne peut-être pas avec l'approche de service à cause d'Android O et des versions ultérieures, comme mentionné par d'autres commentateurs. Cela semble être une solution intéressante pour utiliser des modèles de vue pour cela. Cela se déclenche-t-il toujours si l'application est tuée de force (glissée sur l'écran du sélecteur d'application)? edit: La réponse de Bob suggère que cela fonctionne dans ces cas
William Reed
oui ça marche, c'est le cas avec lequel j'ai rencontré
droidhs
0

Cela a fonctionné pour moi sur android 6,7,8,9.

Faites un service comme celui-ci:

 public class OnClearFromRecentService extends Service {

 @Override public IBinder onBind(Intent intent) {
     return null; }

 @Override public int onStartCommand(Intent intent, int flags, int
 startId) {
     Log.d("ClearFromRecentService", "Service Started");
     return START_NOT_STICKY; }

 @Override public void onDestroy() {
     super.onDestroy();
     Log.d("ClearFromRecentService", "Service Destroyed"); }

 @Override public void onTaskRemoved(Intent rootIntent) {
     Log.e("ClearFromRecentService", "END");
     //Code here
     stopSelf(); } }

2) Enregistrez ce service dans manifest.xml:

<service android:name="com.example.OnClearFromRecentService"
 android:stopWithTask="false" />

3) Ensuite, démarrez ce service sur votre activité de démarrage

 startService(new Intent(getBaseContext(),
 OnClearFromRecentService.class));
Alexis Osorio
la source
1
Et pourquoi votre service continuera-t-il à fonctionner sur Android 8 et supérieur? Vous vous rendez compte qu'il existe des restrictions sur les services démarrés après Android 8, n'est-ce pas?
rahil008
0

Comme Bob Cram l'a mentionné dans sa réponse, la méthode onCleared () de View Model est la réponse. Cela fonctionne dans les deux cas:

  1. Lorsque l'utilisateur supprime l'application en faisant glisser l'application de l'arrière-plan.
  2. Lorsque l'utilisateur efface toute l'application à l'aide du bouton Effacer la liste.

Le service onTaskRemoved () fonctionnera lorsque l'utilisateur balaiera l'application de l'arrière-plan, mais ne fonctionnera pas lorsque les applications sont effacées à l'aide du bouton kill all.

Mais la méthode onCleared () de viewModel fonctionne dans les deux cas. Vous pouvez utiliser if pour arrêter tout processus en cours ou effacer une tâche sur le serveur distant.

 override fun onCleared() {
        super.onCleared()
        Log.d(TAG , "App Killed")
    }
Utkarsh Singh
la source