85 votes

Quel est le bon ordre d'appel des méthodes de superclasse dans les méthodes onPause, onStop et onDestroy? et pourquoi?

J'étais tout simplement en passant par le Développeur Android Site, rafraîchissant sur l'Activité du cycle de Vie, et dans chaque exemple de code, il y a un commentaire à côté de la super-classe de méthodes qui dit: "Toujours appeler la méthode de la superclasse d'abord".

Si cela a un sens dans la création de demi-cycle: onCreate, onStart et onResume, je suis un peu confus quant à ce qui est la procédure correcte sur la destruction de la moitié du cycle : onPause,onStop,onDestroy.

Détruire l'instance de ressources spécifiques tout d'abord, avant de détruire la superclasse des ressources que l'instance spécifique de ressources peut dépendre du sens, pas l'inverse.Mais les observations suggèrent le contraire. Ce qui me manque?

Edit: Puisque les gens semblent obtenir confus quant à l'intention de la question, ce que je veux savoir c'est lequel des énoncés 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();  
    }

97voto

Vikram Points 17845

Détruire l'instance de ressources spécifiques tout d'abord, avant de les détruire superclasse des ressources que l'instance de ressources spécifiques peuvent dépendre sur sens, pas l'inverse. Mais les observations suggèrent sinon. Ce qui me manque?

À mon avis: pas une seule chose.

Cette réponse de Marque (aka CommonsWare sur DONC) fait la lumière sur la question: Lien - si l'appel à la super-classe de la méthode de la première déclaration?. Mais alors, vous pouvez voir la suite à un commentaire laissé sur sa réponse:

Mais pourquoi officiel doc a dit: "Toujours faire appel à la méthode de superclasse première" dans onPause()?

Retour à la case départ. Ok, regardons cela sous un autre angle. Nous savons que Java Langage de Spécification ne permet pas de spécifier un ordre dans lequel l'appel à super.overridenMethod() doit être placée (ou si l'appel doit être placé à tous).

Dans le cas de l'Activité de la classe, super.overridenMethod() des appels sont nécessaires et appliquée:

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

mCalled a la valeur true dans Activity.onStop().

Maintenant, le seul détail à gauche pour un débat sur l'est de la commande.

I also know that both work

Assurez-vous. Regardez le corps de la méthode pour l'Activité.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 méthode sandwich à l'appel d' super.onPause(), vous serez ok. De l'activité.onStop() a le même corps de la méthode. Mais jetez un oeil à l'Activité.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, l'ordre peut , éventuellement, de la matière en fonction de la façon dont votre activité est l'installation, et que vous appeliez super.onDestroy() pourrait interférer avec le code qui suit.

Le mot de la fin, l'énoncé Always call the superclass method first ne semble pas avoir beaucoup de preuves à l'appui. Ce qui est pire (pour l'état), c'est que le code suivant a été prise à partir d' android.app.ListActivity:

public class ListActivity extends Activity {

    ....

    @Override
    protected void onDestroy() {
        mHandler.removeCallbacks(mRequestFocus);
        super.onDestroy();
    }
    ....    
}

Et, à partir de LunarLander exemple d'application inclus dans le sdk android:

public class LunarLander extends Activity {

    ....

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

Résumé et digne les mentions:

L'utilisateur Philip Sheard : Fournit un scénario où un appel à l' super.onPause() doit être retardée en cas d'Activité a démarré à l'aide de startActivityForResult(Intent). Réglage du résultat à l'aide d' setResult(...) après super.onPause() ne fonctionnera pas. Plus tard, il précise à ce sujet dans les commentaires de sa réponse.

L'utilisateur Sherif elKhatib : Explique pourquoi laisser superclasse initialiser ses ressources premières et de détruire ses ressources dernière résulte de la logique:

Considérons une bibliothèque que vous avez téléchargé qui a un LocationActivity qui contient un getLocation() fonction qui fournit l'emplacement. Plus probablement, cette activité devra initialiser ses trucs dans le onCreate() qui va vous forcer à appeler le super.onCreate de la première. Vous déjà le faire parce que vous vous sentez cela a du sens. Maintenant, dans votre onDestroy, vous décidez que vous voulez enregistrer l'Emplacement quelque part dans le SharedPreferences. Si vous appelez super.onDestroy tout d'abord, il est à une certaines mesure du possible que les getLocation retourne une valeur null après cet appel, car la mise en œuvre de LocationActivity annule l'emplacement de la valeur dans le onDestroy. L'idée est que vous ne le blâme pas, si cela arrive. Donc, vous appelez super.onDestroy à la fin après que vous avez terminé avec votre propre onDestroy.

Il va sur un point: si un enfant de la classe est convenablement isolé (en termes de dépendance de la ressource) de la classe parent, l' super.X() des appels n'ont pas besoin d'adhérer à toute spécification de commande.

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

À partir d'une réponse par la Marque:

Méthodes d'ignorer qui font partie de la création de composant (onCreate(), onStart(), onResume(), etc.), vous devriez chaîne à la super-classe comme la première instruction, pour s'assurer que Android a sa chance de faire ses avant de tenter de faire quelque chose qui s'appuie sur ce travail ayant déjà été faite.

Méthodes d'ignorer qui font partie de la composante destruction (onPause(), onStop(), onDestroy(), etc.), vous devriez faire votre travail première et de la chaîne à la super-classe comme la dernière chose. Que ainsi, dans le cas d'Android nettoie quelque chose que votre travail dépend, vous avez fait votre travail d'abord.

Des méthodes qui renvoient à quelque chose autre que void (onCreateOptionsMenu(), etc.), parfois, vous avez de la chaîne d' la super-classe dans l'instruction de retour, en supposant que vous n'êtes pas plus précisément de faire quelque chose qui doit forcer un particulier de retour de la valeur.

Tout le reste-comme onActivityResult() -- est à vous, sur l'ensemble. J'ai tendance à chaîne à la super-classe que la première chose, mais, sauf si vous rencontrez des problèmes, le chaînage doit être plus tard des beaux.

Bob Kerns de ce fil:

C'est un bon modèle [(le motif que la Marque suggère ci-dessus), mais j'ai trouvé quelques exceptions près. Par exemple, le thème que je voulais demander à mon PreferenceActivity ne pas prendre effet, à moins que je l'ai mis avant de la superclasse, onCreate().

L'utilisateur Steve Benett apporte également de l'attention à ceci:

Je ne connais qu'une situation, où le moment de l'appel est super nécessaire. Si vous voulez modifier le comportement standard de la thème ou l'affichage et tel dans onCreate, vous devez le faire avant d'appeler super pour voir un effet. Sinon autant que je sache, il n'y a pas de différence à qui fois que vous l'appelez.

L'utilisateur Sunil Mishra confirme cet ordre (le plus probable) ne joue pas un rôle lors de l'appel de l'Activité de la classe de méthodes. Il affirme également que l'appel de méthodes de la superclasse premier est considéré comme une meilleure pratique. Cependant, je ne pouvais pas le corroborer.

L'utilisateur LOG_TAG : Explique pourquoi un appel à la super-classe constructeur doit être avant tout le reste. À mon avis, cette explication n'ajoute pas à la question posée.

Note de fin: la Confiance, mais vérifier. La plupart des réponses sur cette page, suivez cette approche pour voir si l'instruction Always call the superclass method first a logique de la sauvegarde. Comme il s'avère, il n'a pas; du moins, pas dans le cas de l'Activité de la classe . Généralement, il faut lire par le biais de la super-classe " code source afin de déterminer si la commande des appels à super méthodes est une exigence.

11voto

Sherif elKhatib Points 23987

Depuis (vous dites) il est logique d'appeler super onCreate première: Pensez à ce sujet.

Lorsque je veux créer Mon super crée ses ressources > j'ai créer mes ressources.

Inversement: (une sorte de pile)

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


En ce sens, il s'applique à tout couple de fonctions (onCreate/onDestroy, onResume/onPause, onStart/onStop). Naturellement, onCreate permettra de créer des ressources et des onDestroy permettra de libérer ces ressources. Par ailleurs, la même preuve s'applique pour les autres couples.

Considérons une bibliothèque que vous avez téléchargé qui a un LocationActivity qui contient un getLocation() fonction qui fournit l'emplacement. Plus probablement, cette activité devra initialiser ses trucs dans le onCreate() qui va vous forcer à appeler le super.onCreate de la première. Vous avez déjà le faire parce que vous vous sentez 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 super.onDestroy tout d'abord, il est, dans une certaine mesure du possible que les getLocation retourne une valeur null après cet appel, car la mise en œuvre de LocationActivity annule l'emplacement de la valeur dans le onDestroy. L'idée est que vous ne le blâme pas, si cela arrive. Par conséquent, vous pouvez appeler super.onDestroy à la fin après que vous avez terminé avec votre propre onDestroy. J'espère que ce qui fait un peu de sens.

Si les sens, considérer que, à tout moment, nous avons une activité qui respecte le concept ci-dessus. Si je veux prolonger cette activité, je vais probablement sentir de la même manière et suivent la même commande en raison d'exactement le même argument.

Par induction, toute activité doit faire la même chose. Voici un bon résumé de la classe 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 faire si votre activité est appelée AnudeepBullaActivity s'étend BaseActivity et plus tard, je veux créer SherifElKhatibActivity qui s'étend de votre activité? Dans quel ordre dois-je appeler l' super.do fonctions? C'est finalement la même chose.


Quant à votre question:

Je pense que Google a l'intention de nous dire: Veuillez appeler le super n'importe où. En tant que pratique générale bien sûr, appelez ça au début. Google a bien sûr les plus brillants, les ingénieurs et les développeurs afin qu'ils probablement fait un bon travail en isolant leur super appels et de ne pas interférer dans l'enfant des appels.

J'ai essayé un peu, et ça n'est probablement pas facile (puisque c'est Google nous essayons de prouver la mauvaise) pour créer une activité qui serait panne simple Quand est super d'être appelé.

Pourquoi?

Tout ce qui est fait de ces fonctions est vraiment privé à l'Activité de la classe et ne serait jamais provoquer un 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 les champs privés. Et aucun des public/protected api seront touchés par ce qui est fait ici.

Toutefois, l'API 14, dispatchActivityDestroyed a été ajouté à l'envoi d'une onActivityDestroyed à la ActivityLifecycleCallbacks inscrit à votre Demande. Par conséquent, tout code qui dépendra un peu de logique dans votre ActivityLifecycleCallbacks aura un résultat différent en fonction du moment où vous êtes l'appel de la super. Par exemple:

Créer une Application de Classe qui compte le nombre de actuellement en cours d'exécution des activités:

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) {}
}

La suite n'aurait pas de sens ou n'est pas une bonne pratique, mais c'est juste pour prouver un point (que l'On pourrait trouver un plus réel de la situation). Créer le MainActivity qui va de soi-disant au Revoir à l'activité, quand c'est fini et quand il s'agit de 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 dans le début de votre onDestroy, l'au Revoir de l'activité sera lancée. Si vous appelez super.onDestroy à la fin de votre onDestroy, l'au Revoir de l'activité ne sera pas lancé.

Bien sûr, encore une fois, ce n'est pas la meilleure exemple. Toutefois, cela montre que Google foiré un peu ici. L'un des autres variables seraient pas touchés de votre application à un comportement. Cependant, l'ajout de ces dispatcher le onDestroy causé la super de faire en quelque sorte interférer avec votre sous-classe.

Je dis " ils ont foiré pour une autre raison. Non seulement ont-ils (avant api 14), seulement dans le super appels de ce qui est finale et/ou privé, mais ils ont également appelé les différentes fonctions internes (privé) qui a vraiment ensuite distribué la onPause... fonctions.

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

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.toShortString() +
                    " did not call through to super.onStop()");
        }

        synchronized (mManagedCursors) {
            final int N = mManagedCursors.size();
            for (int i=0; i<N; i++) {
                ManagedCursor mc = mManagedCursors.get(i);
                if (!mc.mReleased) {
                    mc.mCursor.deactivate();
                    mc.mReleased = true;
                }
            }
        }

        mStopped = true;
    }
    mResumed = false;
}

Notez qu'ils appellent l'Activité onStop quelque part dans cette fonction. Par conséquent, ils auraient pu aussi bien mettre tout le code (inclus dans super.onStop) avant ou après l'appel à onStop et puis juste informer les sous-classes sur le onStop à l'aide de vide onStop super fonctions et sans même l'ajout de la SuperNotCalledException ou la vérification de ce qui est appelé.

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

De toute façon c'est la première chose qu'ils font (un peu de mal) et c'est seulement dans l'API 14.

1voto

Sunil Mishra Points 2066

Les DEUX sont corrects OMI

Selon les docs

Les classes dérivées doivent appeler grâce à la super-classe de mise en œuvre de cette méthode. S'ils ne le font pas, une exception sera levée.

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

Vous pouvez toutefois choisir d'appeler la méthode super.

En regardant la source de l' onPause

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

Par conséquent, peu importe, avant ou après, il est appelé. Vous devriez être bon.

Mais pour de meilleures de pratique, vous devriez l'appeler en premier.

Je le recommande surtout comme un mécanisme de protection: si il y a une exception alors l' super méthode d'instance aura déjà été appelé.

Aussi mettre ces appels sur la première ligne vous aidera à éviter de faire des erreurs dans l'avenir, telles que la suppression de code dans la méthode et la suppression accidentelle de l'appel à la super-classe.

1voto

Philip Sheard Points 3721

La chose la plus importante à garder à l'esprit 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 repousser n'importe quel résultat à l'activité parent, vous devez appeler vous-même setResult avant d'appeler super.onPause() . C'est le plus gros problème, à ma connaissance.

1voto

NickT Points 14155

Vous dites que Google suggère la méthode 1, cependant Dianne Hackborn, bien connu Android cadre ingénieur suggèrent le contraire voir Google Lien du Forum.

Il est logique intuitive, à l'appel de la super-classe lors de la destruction d'une instance dans le onPause, onStop et onDestroy méthodes et vice-versa lors de la création d' une instance avec les méthodes onCreate, onResume et de démarrage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X