Quel est l'ordre correct d'appeler les méthodes de superclasse dans les méthodes onPause, onStop et onDestroy? et pourquoi?

89

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

Bien que cela ait du sens dans le demi-cycle de création: onCreate, onStart et onResume, je ne sais pas trop quelle est la procédure correcte sur le demi-cycle de destruction: onPause, onStop, onDestroy.

Il est logique de détruire d'abord les ressources spécifiques à l'instance, avant de détruire les ressources de la superclasse dont les ressources spécifiques à l'instance peuvent dépendre, 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 manière

    @Override
    protected void onStop() {
       //my implementation here

       super.onStop();  
    }
Anudeep Bulla
la source
1
Je suis au camp deux pour les méthodes d'arrêt. Je suis au premier camp pour les méthodes de démarrage.
danny117 du
1
C'est à peu près le point. Je ne pouvais tout simplement pas comprendre à quel point l'utilisation de la méthode 1 pour les méthodes d'arrêt avait du sens.
Anudeep Bulla

Réponses:

107

Détruire d'abord les ressources spécifiques à l'instance, avant de détruire les ressources de superclasse dont les ressources spécifiques à l'instance peuvent dépendre, a du sens, 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 (alias CommonsWare on SO) met en lumière le problème: Lien - L'appel à la méthode superclasse devrait-il être la première déclaration? . Mais alors, vous pouvez voir le commentaire suivant laissé sur sa réponse:

Mais pourquoi la doc officielle dit: "Appelez toujours la méthode de la superclasse en premier" dans onPause ()?

Retour à la case départ. D'accord, regardons cela sous un autre angle. Nous savons que la spécification du langage Java ne spécifie un ordre dans lequel l'appel à super.overridenMethod()doit être placé (ou si l'appel doit être passé du tout).

En cas d'activité de classe, les super.overridenMethod()appels sont obligatoires et appliqués :

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

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

Maintenant, le seul détail qui reste à 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 façon dont vous prenez l'appel super.onPause(), tout ira bien. Activity.onStop () a un corps de méthode similaire. Mais jetez un œ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, la commande pourrait éventuellement importance en fonction de la configuration de votre activité et du fait que l'appel super.onDestroy()interférerait avec le code qui suit.

En guise de dernier mot, la déclaration Always call the superclass method firstne semble pas avoir beaucoup de preuves pour l'étayer. Ce qui est pire (pour l'instruction), c'est que le code suivant a été extrait 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 dignes:

Utilisateur Philip Sheard : fournit un scénario dans lequel un appel à super.onPause()doit être retardé en cas de démarrage d'une activité startActivityForResult(Intent). La définition du résultat en utilisant setResult(...) after super.onPause() ne fonctionnera pas. Il clarifie plus tard ce point dans les commentaires à sa réponse.

Utilisateur Sherif elKhatib : explique pourquoi laisser la superclasse initialiser ses ressources en premier et détruire ses ressources en dernier découle de la logique:

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 pensez que cela a du sens. Maintenant, dans votre onDestroy, vous décidez que vous souhaitez enregistrer l'emplacement quelque part dans 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 le blâmeriez pas si cela se produit.Par conséquent, vous appelleriez super.onDestroy à la fin une fois que vous avez terminé avec votre propre onDestroy.

Il poursuit en soulignant: si une classe enfant est convenablement isolée (en termes de dépendance de ressources) de la classe parente, les super.X()appels ne doivent adhérer à aucune spécification d'ordre.

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

D'après une 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 enchaîner à la superclasse comme première instruction , pour vous assurer qu'Android a sa chance de faire son travail avant vous tenter de faire quelque chose qui repose sur ce travail accompli.

Les méthodes que vous remplacez et qui font partie de la destruction de composants (onPause (), onStop (), onDestroy (), etc.), vous devez d'abord faire votre travail et enchaîner à la superclasse en dernier lieu . De cette façon, au cas où Android nettoie quelque chose dont dépend votre travail, vous aurez 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 doit forcer une valeur de retour particulière.

Tout le reste - comme onActivityResult () - dépend de vous, dans l'ensemble. J'ai tendance à enchaîner à la superclasse comme première chose, mais à moins que vous ne rencontriez des problèmes, le chaînage plus tard devrait être bien.

Bob Kerns de ce fil :

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 à ma 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ù le timing du super appel est nécessaire. Si vous souhaitez modifier le comportement standard du thème ou de l'affichage, etc. dans onCreate, vous devez le faire avant d'appeler super pour voir un effet . Sinon AFAIK il n'y a aucune différence à quel moment vous l'appelez.

L'utilisateur Sunil Mishra confirme que l'ordre (très probablement) ne joue pas de rôle lors de l'appel des méthodes de la classe Activity. Il affirme également qu'appeler d'abord les méthodes de superclasse est considéré comme une meilleure pratique . Cependant, je ne pouvais pas le corroborer.

Utilisateur LOG_TAG : explique pourquoi un appel au constructeur de superclasse doit être avant tout le reste. À 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 firsta un soutien logique. En fait, ce n'est pas le cas; du 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 l'ordre des appels aux méthodes de super est une exigence.

Vikram
la source
2
Sensationnel. Merci pour les pointeurs. Ces réponses et celles de @ Sherif fournissent un contexte important. Si l'un de vous peut résumer les réponses sur cette page, je la marquerai comme acceptée. Veuillez inclure: 1. réponses sur cette page. 2. Réponse de @ Philip sur cette page 3. Réponse de @ CommonsWare sur cette page 4. Cette discussion, je le ferais, mais je ne veux pas les crédits pour vos merveilleuses réponses. Cheers & Thanks
Anudeep Bulla
Salut. Pourriez-vous résumer, étant donné que @Sherif ne le souhaite pas?
Anudeep Bulla
@AnudeepBulla Salut Anudeep, donne-moi jusqu'à demain. Je vais ajouter les éléments pertinents à ma réponse et vous laisser un commentaire ici.
Vikram
@AnudeepBulla J'ai ajouté un résumé ci-dessus. S'il vous plaît faites-le moi savoir au cas où je manquerais quelque chose.
Vikram
@Vikram est le TL; DR. qu'appeler onDestroyet onStopdurer est un défaut plus sûr, et que pour les onPausechoses peuvent être plus délicates dans quelques cas? Pourriez-vous ajouter cela en premier? J'ai été tenté de modifier la réponse moi-même mais je ne suis pas sûr que ce résumé soit correct.
Blaisorblade
12

Puisque (vous dites) il est logique d'appeler d'abord super onCreate: pensez-y.

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

Inversement: (sorte de pile)

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


En ce sens, il s'applique à n'importe quel couple de fonctions (onCreate / onDestroy, onResume / onPause, onStart / onStop). Naturellement, onCreate créera des ressources et onDestroy libérera ces ressources. D'ailleurs, 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 pensez que cela a du sens. Maintenant, dans votre onDestroy, vous décidez que vous souhaitez enregistrer l'emplacement quelque part dans 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 le blâmeriez pas si cela se produit. Par conséquent, vous appelleriez super.onDestroy à la fin une fois que vous en avez terminé avec votre propre onDestroy. J'espère que cela a un peu de 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 étendre cette activité, je ressentirai probablement la même chose et suivrai le même ordre à cause du même argument exact.

Par induction, toute activité doit faire la même chose. Voici une bonne classe abstraite pour une activité obligé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 souhaite créer une SherifElKhatibActivityextension de votre activité? Dans quel ordre dois-je appeler les super.dofonctions? C'est finalement la même chose.


Quant à votre question:

Je pense que l'intention de Google est de nous dire: s'il vous plaît appelez le super, peu importe où. Comme pratique générale, appelez-le au début. Google a bien sûr les ingénieurs et les développeurs les plus brillants, ils ont donc probablement fait du bon travail en isolant leurs super appels et en n'interférant 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 le contraire) de créer une activité qui planterait simplement à cause du super appel de When.

Pourquoi?

Tout ce qui est fait dans ces fonctions est vraiment privé pour la classe Activity et ne provoquerait jamais de 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 l'API 14, dispatchActivityDestroyed a été ajouté pour distribuer un onActivityDestroyed aux ActivityLifecycleCallbacks enregistrés dans votre application. Par conséquent, tout code qui dépendrait d'une logique dans vos ActivityLifecycleCallbacks aura un résultat différent en fonction du moment où 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 va supposément à l'activité GoodBye lorsqu'elle est terminée 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 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. Aucune des autres variables n'aurait affecté le comportement de votre application. Cependant, l'ajout de ces envois à onDestroy a amené le super à interférer d'une manière ou d'une autre avec votre sous-classe.

Je dis qu'ils ont également gâché pour une raison différente. Non seulement ils (avant api 14) n'ont touché que dans les super appels ce qui est final et / ou privé, mais ils ont également appelé différentes fonctions internes (privées) qui ont ensuite vraiment distribué les fonctions onPause ...

Par exemple, performStopfunction est la fonction appelée qui à son tour 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.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 tout aussi bien pu mettre tout le code (inclus dans super.onStop) avant ou après l'appel à onStop, puis simplement notifier les sous-classes à propos de onStop en utilisant des super fonctions onStop vides et sans même ajouter l'exception SuperNotCalledException ou vérifier cette appelée.

Pour cela, s'ils appelaient cette distribution à ActivityLifeCycle dans performDestroy au lieu de l'appeler à la fin de super.onDestroy, le comportement de notre activité aurait été le même quel que soit le moment où 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.

Sherif elKhatib
la source
La question n'a jamais été de savoir pourquoi appeler super.onDestroy () en dernier avait du sens. J'adore votre exemple de bibliothèque. Exactement ce que je voulais transmettre aussi. Je ne pourrais pas être plus d'accord pour appeler les super méthodes en dernier lieu sur les demi-cycles de destruction, exactement comme dans une pile; pour éviter la perte accidentelle de données. Le problème est pourquoi Google insiste-t-il pour appeler d'abord les super méthodes, compte tenu de la prémisse ci-dessus? J'ai posé la question parce que je pensais que peut-être moi, et apparemment vous aussi, peut-être l'aborder complètement différemment. Cheers
Anudeep Bulla
Oh je n'ai pas vu les suggestions de Google et l'inverse: p! Écoutez, je vais essayer de créer une activité qui plantera si vous appelez d'abord onDestroy. Vous devriez essayer cela aussi. Cheers
Sherif elKhatib
@AnudeepBulla, vous pouvez vérifier mes modifications. Et au fait, vous pouvez arrêter d'essayer. super.onne plantera probablement jamais votre activité.
Sherif elKhatib
Sensationnel. Merci pour les pointeurs. Ces réponses et celles de @ User fournissent un contexte important. Si l'un de vous peut résumer les réponses sur cette page, je la marquerai comme acceptée. Veuillez inclure: 1. réponses sur cette page. 2. Réponse de @ Philip sur cette page 3. Réponse de @ CommonsWare sur cette page 4. Cette discussion, je le ferais, mais je ne veux pas les crédits pour vos merveilleuses réponses. Cheers & Thanks
Anudeep Bulla
@AnudeepBulla De rien. Je ne sais pas comment nous allons décider qui publiera la rédaction finale.
Vikram
1

Du point de vue de Java, voici une solution à cette confusion:

Pourquoi this () et super () doivent-ils être la première instruction d'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, passer 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 n'a pas appliqué cela, vous pouvez le faire:

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

Cela montre qu'en fait, les sous-champs doivent être initialisés avant la supreclasse! En attendant, l'exigence java 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 à super est inséré automatiquement pour vous par le compilateur. Puisque chaque classe en Java hérite d'Object, le constructeur d'objets doit être appelé d'une manière ou d'une autre et il doit être exécuté en premier. L'insertion automatique de super () par le compilateur le permet. Faire en sorte que super apparaisse en premier, impose que les corps du constructeur soient exécutés dans le bon ordre qui serait: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

(1) Vérifier que super est la première instruction n'est pas suffisant 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 instruction.

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

Veuillez parcourir ce http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html

LOG_TAG
la source
Je comprends parfaitement ce que vous proposez. Vous appelez d'abord les constructeurs de super classe, «car ils peuvent initialiser les ressources dont l'enfant peut avoir besoin; et les destructeurs durent, parce que vous ne voulez probablement pas effacer tous les parents des ressources locales, ce qui les rend inutiles. C'est exactement ce que je veux dire. Et comme onPause, onStop et onDestroy ont pour tâche de sauvegarder les informations d'état et de rendre plus ou moins les ressources disponibles à GC (donc dans un sens les détruire), je les vois analogues aux destructeurs et je pense donc que les appeler en dernier a du sens. Non?
Anudeep Bulla
Tout ce que vous avez dit ci-dessus est exactement ce que je voulais dire quand j'ai dit "appeler les super méthodes a tout d'abord un sens sur le demi-cycle de création". Je suis préoccupé et confus dans le cas des méthodes de destruction en demi-cycle, qui semblent plus analogues aux destructeurs. Cheers
Anudeep Bulla
1

La chose la plus importante à garder à l'esprit est que cela super.onPause()appelle implicitement setResult(Activity.RESULT_CANCELED). Mais setResultne peut être appelé qu'une seule fois et tous les appels suivants sont ignorés. Donc , si vous voulez pousser tout type de résultat à l'activité parent, vous devez appeler setResultvous - même, avant de vous appeler super.onPause(). C'est le plus gros problème, pour autant que je sache.

Philip Sheard
la source
Wow, cela semble important. Il y a donc une situation où vous devez définitivement retarder l'appel aux super méthodes. Je vous remercie.
Anudeep Bulla
super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED). Pourriez-vous dire d'où vous avez obtenu ça?
Vikram
J'étais confus. C'est en fait finish () qui appelle setResult, et super.onBackPressed () appelle finish (). Donc setResult doit certainement être appelé avant super.onBackPressed (). Je ne sais pas s'il existe des circonstances dans lesquelles super.onPause () pourrait provoquer l'appel de setResult, mais je préfère ne pas risquer.
Philip Sheard
1

LES DEUX sont corrects IMO

Selon la documentation

Les classes dérivées doivent appeler l'implémentation de cette méthode par la super classe. Si ce n'est pas le cas, une exception sera levée.

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

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

En regardant la source de onPause

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

Par conséquent, peu importe avant ou après son appel. Tu devrais être bon.

Mais pour les meilleures pratiques, vous devriez l'appeler en premier.

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

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

Sunil Mishra
la source
Je suis désolé si la question n'était pas tout à fait claire la première fois, mais je vous prie de bien vouloir l'examiner maintenant.
Anudeep Bulla
@AnudeepBulla C'est ce que je vous ai expliqué. Vous pouvez utiliser l'un ou l'autre. Les deux sont valides.
Sunil Mishra le
Je crois comprendre que l'implémentation personnalisée des méthodes onPause, onStop et onDestroy n'est pas strictement nécessaire. J'ai créé de nombreuses applications sans celles-ci. Alors qu'entendez-vous par Super méthodes devraient toujours être appelées? Ils sont appelés implicitement, même sans priorité. Je sais aussi que les deux fonctionnent. Je veux savoir pourquoi les docs disent que super devrait être appelé en premier. Et au cas où la question ne serait toujours pas apparente, pourriez-vous expliquer POURQUOI lorsque vous dites "Mais pour les meilleures pratiques, vous devriez l'appeler en premier"?
Anudeep Bulla
Si vous ne remplacez pas, les méthodes sont appelées à partir de Base Classmais si vous remplacez, il est nécessaire que vous appeliez l' superautre que vous aurezandroid.app.SuperNotCalledException
Sunil Mishra
1
Vous semblez mal comprendre. La question n'est pas de savoir s'il faut appeler ou non. Comme vous l'avez souligné, vous DEVEZ, si vous les remplacez. La question est, quand?
Anudeep Bulla
1

Vous dites que Google suggère la méthode 1, mais Dianne Hackborn, un ingénieur de framework Android bien connu suggère sinon voir le lien du forum Google .

Il est intuitif d'appeler la super classe en dernier lors de la 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 .

NickT
la source
Le lien avec le post de Dianne Hackborn est important et confirme le schéma.
Nick Westgate le
0

Le super des rappels est nécessaire pour mettre 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. Vous pouvez maintenant le remplacer et par exemple charger votre mise en page. Mais dans l'intérêt du flux du système, vous devez appeler super, pour que le système puisse continuer avec la procédure standard. C'est pourquoi une exception sera levé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 n'importe quel rappel et l'activité serait interceptée dans celui-là. Ainsi, le système sait quand le rappel est terminé et appelle le suivant.

Je ne connais qu'une seule situation, où le timing du super appel est nécessaire. Si vous souhaitez modifier le comportement standard du thème ou de l'affichage, etc. dans onCreate, vous devez le faire avant d'appeler super pour voir un effet. Sinon AFAIK il n'y a aucune différence à quel moment vous l'appelez.

Mais pour laisser le système faire ce qu'il peut le mieux, mettez le super dans la première ligne d'un rappel suivi de votre code, si vous n'avez pas de bonne raison de rompre avec lui.

Steve Benett
la source
La séquence dans onCreate semble assez compréhensible. Que se passe-t-il dans les méthodes de destruction? Dites onStop. Supposons que mon implémentation onStop utilise certaines des ressources que la super méthode libère si elle est appelée. Il est donc logique d'appeler la super méthode après mon implémentation.
Anudeep Bulla
Idéalement, cela devrait être le cas, non. Notre activité aura toujours des ressources dont dispose la super-classe, et d'autres encore, et celles qui sont indépendantes de mon activité peuvent, dans la plupart des cas, dépendre des ressources de la superclasse, qui sont communes. Il est plus logique de traiter d'abord mes ressources, puis d'appeler la superclasse pour s'occuper des plus courantes. Pourquoi Google dit-il alors que nous "DEVONS" appeler d'abord les méthodes de superclasse?
Anudeep Bulla
De quelle ressource parlez-vous, à laquelle vous pouvez accéder dans onCreate mais pas dans onDestroy?
Steve Benett
Je n'ai pas de cas d'utilisation. Je me demande simplement comment cela varie avec le style POO. Les constructeurs de super classes sont appelés en premier, avant votre implémentation, et les destructeurs de super classes sont appelés en dernier, après votre implémentation, n'est-ce pas? onPause, onStop et onDestroy ne sont pas strictement des destructeurs, mais ils ont tendance à faire la même chose, non? Au moins onDestroy et surtout onStop aussi .. non?
Anudeep Bulla
Voir les rappels dans la manière de changer d'état non comme un constructeur / destructeur. Chaque rappel a son besoin, par exemple créer (prêt à l'emploi) / détruire (votre dernière chance d'interagir) une activité ou la mettre au premier plan / en arrière-plan. Les rappels sont là pour que vous puissiez contrôler vos ressources dans le flux du système. Le système vérifie simplement dans quel état il se trouve et le gère en conséquence. Les ressources que vous utilisez et celle que le système contrôle sont indépendantes les unes des autres et il n'y aura pas d'intersection.
Steve Benett le