Quel est l’ordre correct de l’appel des méthodes de la super-classe dans les méthodes onPause, onStop et onDestroy? et pourquoi?

J’étais en train de parcourir le site de développement Android, en actualisant le cycle de vie de l’activité, et dans chaque exemple de code, il y a un commentaire à côté des méthodes de la super classe qui dit “Appelez toujours la méthode superclasse en premier”.

Bien que cela ait un sens dans le demi-cycle de création: onCreate, onStart et onResume, je suis un peu perplexe quant à la procédure correcte sur le demi-cycle de destruction: onPause, onStop, onDestroy.

Détruire d’abord les ressources spécifiques à l’instance, avant de détruire les ressources de super-classe dont dépendent les ressources spécifiques à l’instance, a du sens, et non l’inverse. Mais les commentaires suggèrent le contraire. Qu’est-ce que je rate?

Edit : Puisque les gens semblent être confus quant à l’intention de la question, ce que je veux savoir, c’est lequel des éléments suivants est correct? ET POURQUOI ?

1. Google suggère

@Override protected void onStop() { super.onStop(); // Always call the superclass method first //my implementation here } 

2. L’autre façon

  @Override protected void onStop() { //my implementation here super.onStop(); } 

Détruire les ressources spécifiques à l’instance avant de détruire les ressources de la superclasse dont dépendent les ressources spécifiques à l’instance est logique, et non l’inverse. Mais les commentaires suggèrent le contraire. Qu’est-ce que je rate?

A mon avis: pas une seule chose.

Cette réponse de Mark (aka CommonsWare sur SO) met en lumière la question: Link – L’appel à la méthode superclasse devrait-il être la première déclaration? . Mais alors, vous pouvez voir le commentaire suivant sur sa réponse:

Mais pourquoi doc officiel dit: “Appelez toujours la méthode superclass en premier” dans onPause ()?

Retour à la case départ. Ok, regardons ça sous un autre angle. Nous soaps que Java Language Specification ne spécifie pas un ordre dans lequel l’appel à super.overridenMethod() doit être placé (ou si l’appel doit être placé du tout).

Dans le cas de la classe Activity, les super.overridenMethod() sont requirejs et appliqués :

 if (!mCalled) { throw new SuperNotCalledException( "Activity " + mComponent.toShortSsortingng() + " did not call through to super.onStop()"); } 

mCalled est défini sur true dans Activity.onStop() .

Maintenant, le seul détail laissé à débattre est la commande.

I also know that both work

Sûr. Regardez le corps de la méthode pour Activity.onPause ():

 protected void onPause() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onPause " + this); // This is to invoke // Application.ActivityLifecyleCallbacks.onActivityPaused(Activity) getApplication().dispatchActivityPaused(this); // The flag to enforce calling of this method mCalled = true; } 

Quelle que soit la manière dont vous appelez l’appel à super.onPause() , vous serez d’accord. Activity.onStop () a un corps de méthode similaire. Mais jetez un coup d’œil à Activity.onDestroy ():

 protected void onDestroy() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this); mCalled = true; // dismiss any dialogs we are managing. if (mManagedDialogs != null) { final int numDialogs = mManagedDialogs.size(); for (int i = 0; i < numDialogs; i++) { final ManagedDialog md = mManagedDialogs.valueAt(i); if (md.mDialog.isShowing()) { md.mDialog.dismiss(); } } mManagedDialogs = null; } // close any cursors we are managing. synchronized (mManagedCursors) { int numCursors = mManagedCursors.size(); for (int i = 0; i < numCursors; i++) { ManagedCursor c = mManagedCursors.get(i); if (c != null) { c.mCursor.close(); } } mManagedCursors.clear(); } // Close any open search dialog if (mSearchManager != null) { mSearchManager.stopSearch(); } getApplication().dispatchActivityDestroyed(this); } 

Ici, le classement peut dépendre de la configuration de votre activité et du fait que l'appel de super.onDestroy() interfère avec le code qui suit.

En dernier mot, la déclaration Always call the superclass method first ne semble pas avoir beaucoup de preuves pour la sauvegarder. Ce qui est pire (pour la déclaration) est que le code suivant a été pris de android.app.ListActivity :

 public class ListActivity extends Activity { .... @Override protected void onDestroy() { mHandler.removeCallbacks(mRequestFocus); super.onDestroy(); } .... } 

Et, à partir de l'exemple d'application LunarLander inclus dans Android sdk:

 public class LunarLander extends Activity { .... @Override protected void onPause() { mLunarView.getThread().pause(); // pause game when Activity pauses super.onPause(); } .... } 

Résumé et mentions valables:

Utilisateur Philip Sheard : fournit un scénario dans lequel un appel à super.onPause() doit être retardé en cas de démarrage d'une activité à l'aide de startActivityForResult(Intent) . Définir le résultat à l'aide de setResult(...) après super.onPause() ne fonctionnera pas. Il clarifie plus tard à ce sujet dans les commentaires de sa réponse.

Utilisateur Sherif elKhatib : Explique pourquoi laisser la superclasse initialiser ses ressources en premier et détruire ses ressources résulte de la logique suivante:

Considérons une bibliothèque que vous avez téléchargée qui a une LocationActivity qui contient une fonction getLocation () qui fournit l'emplacement. Très probablement, cette activité devra initialiser son contenu dans onCreate (), ce qui vous obligera à appeler le super.onCreate en premier . Vous faites déjà cela parce que vous sentez que cela a du sens. Maintenant, dans votre onDestroy, vous décidez que vous voulez enregistrer l'emplacement quelque part dans les SharedPreferences. Si vous appelez d'abord super.onDestroy, il est dans une certaine mesure possible que getLocation renvoie une valeur nulle après cet appel car l'implémentation de LocationActivity annule la valeur d'emplacement dans onDestroy. L'idée est que vous ne lui en voudriez pas si cela se produisait. Par conséquent, vous appelez super.onDestroy à la fin après avoir terminé votre propre onDestroy.

Il poursuit en super.X() : si une classe enfant est suffisamment isolée (en termes de dépendance vis-à-vis des ressources) par rapport à la classe parente, les super.X() ne doivent respecter aucune spécification d'ordre.

Voir sa réponse sur cette page pour lire un scénario où le placement de l'appel super.onDestroy() affecte la logique du programme.

De la réponse de Mark :

Les méthodes que vous remplacez et qui font partie de la création de composants (onCreate (), onStart (), onResume (), etc.), vous devez les enchaîner à la superclasse en tant que première déclaration pour vous assurer qu'Android a la possibilité de faire son travail essayer de faire quelque chose qui repose sur ce travail a été fait.

Les méthodes que vous remplacez et qui font partie de la destruction des composants (onPause (), onStop (), onDestroy (), etc.), vous devez commencer par travailler et enchaîner avec la super-classe . De cette façon, si Android nettoie quelque chose dont dépend votre travail, vous aurez tout d'abord fait votre travail.

Les méthodes qui renvoient autre chose que void (onCreateOptionsMenu (), etc.), parfois vous enchaînez à la superclasse dans l'instruction return, en supposant que vous ne faites pas spécifiquement quelque chose qui oblige une valeur de retour particulière.

Tout le rest - comme onActivityResult () - dépend de vous, dans l’ensemble. J'ai tendance à enchaîner avec la super-classe en premier lieu, mais à moins que vous ne renconsortingez des problèmes, le chaînage ultérieur devrait convenir.

Bob Kerns de cette discussion :

C'est un bon modèle [(le modèle que Mark suggère ci-dessus)], mais j'ai trouvé quelques exceptions. Par exemple, le thème que je voulais appliquer à PreferenceActivity ne prendrait effet que si je le mettais avant onCreate () de la superclasse.

L'utilisateur Steve Benett attire également l'attention sur ceci:

Je ne connais qu'une seule situation où la synchronisation de la super-appel est nécessaire. Si vous voulez modifier le comportement standard du thème ou de l'affichage, comme dans onCreate, vous devez le faire avant d'appeler super pour voir un effet . Sinon, il n'y a pas de différence à quelle heure vous l'appelez.

L'utilisateur Sunil Mishra confirme que l'ordre (le plus probable) ne joue aucun rôle lors de l'appel des méthodes de la classe d'activité. Il affirme également que l' appel des méthodes de la superclasse en premier est considéré comme une pratique exemplaire . Cependant, je ne pouvais pas le confirmer.

User LOG_TAG : Explique pourquoi un appel à un constructeur de superclasses doit être avant tout le rest. À mon avis, cette explication n’ajoute rien à la question posée.

Note de fin : Faites confiance, mais vérifiez. La plupart des réponses sur cette page suivent cette approche pour voir si l'instruction Always call the superclass method first support logique. Il s'avère que ce n’est pas le cas; au moins, pas dans le cas de l'activité de classe. Généralement, il faut lire le code source de la superclasse pour déterminer si la commande d'appels aux méthodes de super est une exigence.

Depuis (vous dites), il est logique d’appeler Super onCreate en premier: Pensez-y.

Quand je veux créer, mon super crée ses ressources> je crée mes ressources.

Inversement: (sorte de stack)

Quand je veux détruire, je détruis mes ressources> Mon super détruit ses ressources.


En ce sens, elle s’applique à quelques fonctions (onCreate / onDestroy, onResume / onPause, onStart / onStop). Naturellement, onCreate va créer des ressources et onDestroy va libérer ces ressources. Au fait, la même preuve s’applique aux autres couples.

Considérons une bibliothèque que vous avez téléchargée qui a une LocationActivity qui contient une fonction getLocation () qui fournit l’emplacement. Très probablement, cette activité devra initialiser son contenu dans onCreate (), ce qui vous obligera à appeler le super.onCreate en premier. Vous faites déjà cela parce que vous sentez que cela a du sens. Maintenant, dans votre onDestroy, vous décidez que vous voulez enregistrer l’emplacement quelque part dans les SharedPreferences. Si vous appelez d’abord super.onDestroy, il est dans une certaine mesure possible que getLocation renvoie une valeur nulle après cet appel car l’implémentation de LocationActivity annule la valeur d’emplacement dans onDestroy. L’idée est que vous ne lui en voudriez pas si cela se produisait. Par conséquent, vous appelez super.onDestroy à la fin après avoir terminé votre propre onDestroy. J’espère que cela fait un peu sens.

Si ce qui précède a du sens, considérez qu’à tout moment nous avons une activité qui respecte le concept ci-dessus. Si je veux prolonger cette activité, je vais probablement ressentir la même chose et suivre le même ordre à cause du même argument exact.

Par induction, toute activité devrait faire la même chose. Voici un bon cours abstrait pour une activité forcée de suivre ces règles:

 package mobi.sherif.base; import android.app.Activity; import android.os.Bundle; public abstract class BaseActivity extends Activity { protected abstract void doCreate(Bundle savedInstanceState); protected abstract void doDestroy(); protected abstract void doResume(); protected abstract void doPause(); protected abstract void doStart(); protected abstract void doStop(); protected abstract void doSaveInstanceState(Bundle outState); @Override protected final void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); doCreate(savedInstanceState); } @Override protected final void onDestroy() { doDestroy(); super.onDestroy(); } @Override protected final void onResume() { super.onResume(); doResume(); } @Override protected final void onPause() { doPause(); super.onPause(); } @Override protected final void onStop() { doStop(); super.onStop(); } @Override protected final void onStart() { super.onStart(); doStart(); } @Override protected final void onSaveInstanceState(Bundle outState) { doSaveInstanceState(outState); super.onSaveInstanceState(outState); } } 

Enfin, que se passe-t-il si votre activité appelée AnudeepBullaActivity étend BaseActivity et plus tard, je veux créer SherifElKhatibActivity qui étend votre activité? Dans quel ordre dois-je appeler les fonctions super.do ? C’est finalement la même chose.


Pour votre question:

Je pense que l’intention de Google est de nous dire: Appelez le super peu importe où. Comme une pratique générale bien sûr, appelez-le au début. Bien sûr, Google a les ingénieurs et les développeurs les plus shinys, alors ils ont probablement fait du bon travail en isolant leurs super-appels et en n’intervenant pas dans les appels enfants.

J’ai essayé un peu et ce n’est probablement pas facile (puisque c’est Google que nous essayons de prouver que c’est faux) de créer une activité qui tomberait en panne à cause de When est super appelé.

Pourquoi?

Tout ce qui est fait dans ces fonctions est vraiment privé à la classe Activity et ne provoquerait aucun conflit avec votre sous-classe. Par exemple (onDestroy)

 protected void onDestroy() { if (DEBUG_LIFECYCLE) Slog.v(TAG, "onDestroy " + this); mCalled = true; // dismiss any dialogs we are managing. if (mManagedDialogs != null) { final int numDialogs = mManagedDialogs.size(); for (int i = 0; i < numDialogs; i++) { final ManagedDialog md = mManagedDialogs.valueAt(i); if (md.mDialog.isShowing()) { md.mDialog.dismiss(); } } mManagedDialogs = null; } // close any cursors we are managing. synchronized (mManagedCursors) { int numCursors = mManagedCursors.size(); for (int i = 0; i < numCursors; i++) { ManagedCursor c = mManagedCursors.get(i); if (c != null) { c.mCursor.close(); } } mManagedCursors.clear(); } // Close any open search dialog if (mSearchManager != null) { mSearchManager.stopSearch(); } getApplication().dispatchActivityDestroyed(this); } 

mManagedCursors et mManagedDialogs et mSearchManager sont tous des champs privés. Et aucune des API publiques / protégées ne sera affectée par ce qui est fait ici.

Cependant, dans API 14, dispatchActivityDestroyed a été ajouté pour envoyer un onActivityDestroyed aux ActivityLifecycleCallbacks enregistrés dans votre application. Par conséquent, tout code qui dépend de la logique de votre ActivityLifecycleCallback aura un résultat différent selon que vous appelez le super. Par exemple:

Créez une classe d'application qui compte le nombre d'activités en cours d'exécution:

 package mobi.shush; import android.app.Activity; import android.app.Application; import android.app.Application.ActivityLifecycleCallbacks; import android.os.Bundle; public class SherifApplication extends Application implements ActivityLifecycleCallbacks { @Override public void onCreate() { super.onCreate(); registerActivityLifecycleCallbacks(this); } public int getCount() { return count; } int count = 0; @Override public void onActivityCreated(Activity activity, Bundle savedInstanceState) { count++; } @Override public void onActivityDestroyed(Activity activity) { count--; } @Override public void onActivityPaused(Activity activity) {} @Override public void onActivityResumed(Activity activity) {} @Override public void onActivitySaveInstanceState(Activity activity, Bundle outState) {} @Override public void onActivityStarted(Activity activity) {} @Override public void onActivityStopped(Activity activity) {} } 

Ce qui suit peut ne pas avoir de sens ou n'est pas celui d'une bonne pratique mais c'est juste pour prouver un point (on pourrait trouver une situation plus réelle). Créez la mainActivity qui est censée aller à l'activité GoodBye quand elle est terminée et quand c'est la dernière activité:

 @Override protected void onDestroy() { super.onDestroy(); if(((SherifApplication) getApplication()).getCount() == 0) { //i want to go to a certain activity when there are no other activities startActivity(new Intent(this, GoodBye.class)); } } 

Si vous appelez super.onDestroy au début de votre onDestroy, l'activité GoodBye sera lancée. Si vous appelez super.onDestroy à la fin de votre onDestroy, l'activité GoodBye ne sera pas lancée.

Bien sûr, encore une fois, ce n'est pas l'exemple optimal. Cependant, cela montre que Google a un peu foiré ici. N'importe laquelle des autres variables n'aurait pas affecté le comportement de votre application. Cependant, l'ajout de ces envois à onDestroy a provoqué des interférences entre le super et votre sous-classe.

Je dis qu'ils ont également joué pour une raison différente. Non seulement ils (avant api 14) ne touchent que les appels ultimes ce qui est final et / ou privé, mais ils appellent également différentes fonctions internes (privées) qui diffusent alors vraiment les fonctions onPause ....

Par exemple, la fonction performStop est la fonction appelée à son tour qui appelle la fonction onStop:

 final void performStop() { if (mLoadersStarted) { mLoadersStarted = false; if (mLoaderManager != null) { if (!mChangingConfigurations) { mLoaderManager.doStop(); } else { mLoaderManager.doRetain(); } } } if (!mStopped) { if (mWindow != null) { mWindow.closeAllPanels(); } if (mToken != null && mParent == null) { WindowManagerGlobal.getInstance().setStoppedState(mToken, true); } mFragments.dispatchStop(); mCalled = false; mInstrumentation.callActivityOnStop(this); if (!mCalled) { throw new SuperNotCalledException( "Activity " + mComponent.toShortSsortingng() + " did not call through to super.onStop()"); } synchronized (mManagedCursors) { final int N = mManagedCursors.size(); for (int i=0; i 

Notez qu'ils appellent l'activité onStop quelque part dans cette fonction. Par conséquent, ils peuvent aussi bien mettre tout le code (inclus dans super.onStop) avant ou après l'appel sur onStop, puis simplement notifier les sous-classes du onStop en utilisant les super-fonctions vides onStop et sans même append SuperNotCalledException ou vérifier cet appel.

Pour cela, s'ils appelaient cette dissortingbution à ActivityLifeCycle dans performDestroy au lieu de l'appeler à la fin de super.onDestroy, le comportement de notre activité aurait été le même peu importe quand nous avons appelé le super.

Quoi qu’il en soit, c’est la première chose qu’ils font (un peu mal) et ce n’est que dans l’API 14.

La chose la plus importante à super.onPause() est que super.onPause() appelle implicitement setResult(Activity.RESULT_CANCELED) . Mais setResult ne peut être appelé qu’une seule fois et tous les appels suivants sont ignorés. Donc, si vous voulez renvoyer n’importe quel type de résultat à l’activité parente, vous devez appeler setResult avant d’appeler super.onPause() . C’est le plus gros truc, à ma connaissance.

Les deux sont correctes OMI

Selon les docs

Les classes dérivées doivent faire appel à l’implémentation de cette méthode par la super classe. S’ils ne le font pas, une exception sera lancée.

Super méthode Super devrait toujours être appelée lorsque la documentation le dit explicitement.

Vous pouvez cependant choisir quand appeler la méthode super.

En regardant la source de onPause

 protected void onPause() { getApplication().dispatchActivityPaused(this); mCalled = true; } 

D’où peu importe avant ou après qu’il soit appelé. Vous devriez être bon.

Mais pour de meilleures pratiques, vous devriez d’abord l’appeler.

Je le recommande surtout comme mécanisme de protection: s’il y a une exception alors la méthode de la super instance aura déjà été appelée.

En outre, placer ces appels sur la première ligne vous évitera de commettre des erreurs à l’avenir, telles que la suppression du code dans la méthode et la suppression accidentelle de l’appel à la super-classe.

Vous dites que Google suggère la méthode 1, mais Dianne Hackborn, un ingénieur-cadre Android bien connu, suggère que le lien vers le forum ne soit pas publié .

Il est logique d’appeler la classe super dernière en cas de destruction d’ une instance dans les méthodes onPause, onStop et onDestroy et d’ abord lors de la création d’ une instance avec les méthodes onCreate, onResume et onStart .

Le super des rappels est nécessaire pour placer l’activité dans le bon état en interne pour le système.

Disons que vous démarrez votre activité et que onCreate est appelé par le système. Maintenant, vous pouvez le remplacer et charger par exemple votre mise en page. Mais dans l’intérêt du stream système, vous devez appeler super, que le système peut continuer avec la procédure standard. C’est pourquoi une exception sera lancée si vous ne l’appelez pas.

Cela se produit indépendamment de votre implémentation dans onCreate. C’est seulement important pour le système. S’il n’y avait pas d’ANR, vous pourriez avoir une boucle sans fin dans tout rappel et l’activité serait interceptée dans celle-ci. Ainsi, le système sait quand le rappel a été terminé et appelle le suivant.

Je ne connais qu’une seule situation où la synchronisation de la super-appel est nécessaire. Si vous voulez modifier le comportement standard du thème ou de l’affichage, comme dans onCreate, vous devez le faire avant d’appeler super pour voir un effet. Sinon, il n’y a pas de différence à quelle heure vous l’appelez.

Mais pour laisser le système faire ce qu’il peut, placez le super dans la première ligne d’un callback suivi de votre code, si vous n’avez pas une bonne raison de le rompre.

Du sharepoint vue Java, voici une solution à cette confusion:

Pourquoi this () et super () doivent-ils être la première instruction dans un constructeur?

Le constructeur de la classe parente doit être appelé avant le constructeur de la sous-classe. Cela garantira que si vous appelez des méthodes sur la classe parente dans votre constructeur, la classe parente a déjà été configurée correctement.

Ce que vous essayez de faire, transmettre des arguments au super constructeur est parfaitement légal, il vous suffit de construire ces arguments en ligne comme vous le faites, ou de les transmettre à votre constructeur, puis de les transmettre à super:

 public MySubClassB extends MyClass { public MySubClassB(Object[] myArray) { super(myArray); } } 

Si le compilateur ne l’a pas appliqué, vous pouvez le faire:

 public MySubClassB extends MyClass { public MySubClassB(Object[] myArray) { someMethodOnSuper(); //ERROR super not yet constructed super(myArray); } } 

Cela montre qu’en réalité, les sous-champs doivent être inilialisés avant la supréclass! Pendant ce temps, java exigences “nous défend” de la spécialisation de la classe en spécialisant ce que l’argument du super constructeur

Dans les cas où une classe parente a un constructeur par défaut, l’appel au super est automatiquement inséré par le compilateur. Étant donné que chaque classe de Java hérite de Object, le constructeur d’objects doit être appelé en quelque sorte et il doit être exécuté en premier. L’insertion automatique de super () par le compilateur le permet. Appliquer super pour apparaître en premier, impose que les corps de constructeurs soient exécutés dans le bon ordre qui serait: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

(1) Vérifier que super est la première déclaration ne suffit pas pour éviter ce problème. Par exemple, vous pouvez mettre “super (someMethodInSuper ());” dans votre constructeur. Cela tente d’accéder à une méthode dans la superclasse avant sa construction, même si super est la première déclaration.

(2) Le compilateur semble implémenter une vérification différente, suffisante pour éviter ce problème. Le message est “impossible de faire référence à xxx avant que le constructeur de supertype ait été appelé”. Par conséquent, vérifier que super est la première déclaration n’est pas nécessaire

S’il vous plaît passer par cette http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html