Jaka jest prawidłowa kolejność wywoływania metod nadklasy w metodach onPause, onStop i onDestroy? i dlaczego?

89

Właśnie przeglądałem witrynę programistów Androida, odświeżając cykl życia działania, aw każdym przykładzie kodu obok metod superklasy znajduje się komentarz, który mówi: „Zawsze najpierw wywołuj metodę nadklasy”.

Chociaż ma to sens w półcyklu tworzenia: onCreate, onStart i onResume, jestem trochę zdezorientowany, jaka jest prawidłowa procedura na półcyklu niszczenia: onPause, onStop, onDestroy.

Zniszczenie najpierw zasobów specyficznych dla instancji, przed zniszczeniem zasobów nadklasy, od których mogą zależeć zasoby instancji, ma sens, a nie odwrotnie, ale komentarze sugerują inaczej. czego mi brakuje?

Edycja : Ponieważ ludzie wydają się być zdezorientowani co do intencji w pytaniu, chcę wiedzieć, które z poniższych zdań jest poprawne? I DLACZEGO ?

1. sugeruje Google

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

      //my implementation here
    }

2. w inny sposób

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

       super.onStop();  
    }
Anudeep Bulla
źródło
1
Jestem w obozie drugim dla metod zamykania. Jestem na pierwszym obozie dla metod uruchamiania.
danny117
1
O to właśnie chodzi. Po prostu nie mogłem zrozumieć, jak użycie metody 1 do metod zamykania ma sens.
Anudeep Bulla

Odpowiedzi:

107

Zniszczenie najpierw zasobów specyficznych dla instancji, przed zniszczeniem zasobów nadklasy, od których mogą zależeć zasoby instancji, ma sens, a nie odwrotnie. Ale komentarze sugerują inaczej. czego mi brakuje?

Moim zdaniem: ani jednej rzeczy.

Ta odpowiedź Marka (aka CommonsWare na SO) rzuca światło na ten problem: Link - Czy wywołanie metody nadklasy powinno być pierwszym stwierdzeniem? . Ale wtedy możesz zobaczyć następujący komentarz pozostawiony przy jego odpowiedzi:

Ale dlaczego oficjalny dokument mówi: „Zawsze najpierw wywołuj metodę nadklasy” w onPause ()?

Wróćmy do punktu wyjścia. Dobra, spójrzmy na to z innej strony. Wiemy, że specyfikacja języka Java nie określa kolejności, w jakiej super.overridenMethod()należy umieścić wywołanie (lub czy w ogóle musi ono zostać wykonane).

W przypadku zajęć klasowych super.overridenMethod()wezwania są wymagane i egzekwowane :

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

mCalledjest ustawiony na true w Activity.onStop().

Teraz jedyny szczegół do omówienia to kolejność.

I also know that both work

Pewnie. Spójrz na treść metody dla 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;
}

Niezależnie od sposobu, w jaki przełączysz telefon super.onPause(), wszystko będzie w porządku. Activity.onStop () ma podobną treść metody. Ale spójrz na 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);
}

W tym przypadku kolejność może mieć znaczenie w zależności od konfiguracji Twojej aktywności i tego, czy wywołanie super.onDestroy()mogłoby kolidować z kodem, który następuje.

Na koniec stwierdzenie Always call the superclass method firstwydaje się nie mieć zbyt wielu dowodów na poparcie tego. Co gorsza (dla oświadczenia) jest to, że poniższy kod zaczerpnięto z android.app.ListActivity:

public class ListActivity extends Activity {

    ....

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

A z przykładowej aplikacji LunarLander zawartej w pakiecie SDK dla systemu Android:

public class LunarLander extends Activity {

    ....

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

Podsumowanie i godne uwagi:

Użytkownik Philip Sheard : zapewnia scenariusz, w którym połączenie z super.onPause()musi zostać opóźnione w przypadku rozpoczęcia działania startActivityForResult(Intent). Ustawienie wyniku za pomocą setResult(...) after super.onPause() nie zadziała. Później wyjaśnia to w komentarzach do swojej odpowiedzi.

Użytkownik Sherif elKhatib : Wyjaśnia, dlaczego pozwolenie superklasie na inicjalizację jej zasobów w pierwszej kolejności i zniszczenie jej na końcu wynika z logiki:

Rozważmy pobraną bibliotekę, która ma funkcję LocationActivity zawierającą funkcję getLocation (), która zapewnia lokalizację. Najprawdopodobniej to działanie będzie musiało zainicjować swoje rzeczy w onCreate (), co zmusi cię do wywołania najpierw super.onCreate . Już to robisz, bo czujesz, że to ma sens. Teraz w swoim onDestroy decydujesz, że chcesz zapisać lokalizację gdzieś w SharedPreferences. Jeśli najpierw wywołasz super.onDestroy, jest do pewnego stopnia możliwe, że getLocation zwróci wartość null po tym wywołaniu, ponieważ implementacja LocationActivity unieważnia wartość lokalizacji w onDestroy. Chodzi o to, że nie możesz tego winić, jeśli tak się stanie.Dlatego na koniec nazwałbyś super.onDestroy, gdy skończysz z własnym onDestroy.

Następnie super.X()zwraca uwagę: jeśli klasa potomna jest odpowiednio odizolowana (pod względem zależności zasobów) od klasy nadrzędnej, wywołania nie muszą być zgodne z żadną specyfikacją kolejności.

Zobacz jego odpowiedź na tej stronie, aby zapoznać się z sytuacji, w której umiejscowienie super.onDestroy()połączenia nie wpływa na logikę programu.

Z odpowiedzi Marka :

Metody, które nadpisujesz, które są częścią tworzenia komponentów (onCreate (), onStart (), onResume () itp.), Powinieneś połączyć się z nadklasą jako pierwszą instrukcją , aby upewnić się, że Android ma szansę wykonać swoją pracę przed tobą podjąć próbę zrobienia czegoś, co jest uzależnione od wykonania tej pracy.

W przypadku metod, które nadpisujesz, które są częścią niszczenia komponentów (onPause (), onStop (), onDestroy () itp.), Powinieneś najpierw wykonać swoją pracę i połączyć się z nadklasą jako ostatnią rzeczą . W ten sposób, jeśli Android wyczyści coś, od czego zależy Twoja praca, najpierw wykonasz swoją pracę.

Metody, które zwracają coś innego niż void (onCreateOptionsMenu () itp.), Czasami w instrukcji return łańcuchowo kierujesz do nadklasy, zakładając, że nie robisz czegoś, co musi wymusić określoną wartość zwracaną.

Wszystko inne - takie jak onActivityResult () - ogólnie zależy od Ciebie. Zwykle najpierw łączę się z superklasą, ale jeśli nie napotkasz problemów, późniejsze łańcuchy powinny wystarczyć.

Bob Kerns z tego wątku :

To dobry wzór [(wzór, który sugeruje Mark powyżej)], ale znalazłem kilka wyjątków. Na przykład motyw, który chciałem zastosować do mojej PreferenceActivity, nie zadziałał, chyba że umieściłbym go przed onCreate () klasy nadrzędnej.

Użytkownik Steve Benett również zwraca uwagę na to:

Znam tylko jedną sytuację, w której konieczne jest wykonanie super rozmowy. Jeśli chcesz zmienić standardowe zachowanie motywu lub wyświetlacza itp. W onCreate, musisz to zrobić, zanim zadzwonisz do super, aby zobaczyć efekt . W przeciwnym razie AFAIK nie ma różnicy, w którym momencie to nazwiesz.

Użytkownik Sunil Mishra potwierdza, że ​​kolejność (najprawdopodobniej) nie odgrywa roli podczas wywoływania metod klasy Activity. Twierdzi również, że wywołanie najpierw metod nadklasy jest uważane za najlepszą praktykę . Jednak nie mogłem tego potwierdzić.

User LOG_TAG : wyjaśnia, dlaczego wywołanie konstruktora nadklasy musi być poprzedzające wszystko inne. Moim zdaniem to wyjaśnienie nie dodaje do zadawanego pytania.

Uwaga końcowa: ufaj, ale weryfikuj. Większość odpowiedzi na tej stronie jest zgodnych z tym podejściem, aby sprawdzić, czy instrukcja Always call the superclass method firstma logiczną podstawę. Jak się okazuje, tak nie jest; przynajmniej nie w przypadku zajęć z zajęć. Ogólnie rzecz biorąc, należy przeczytać kod źródłowy nadklasy, aby określić, czy zamówienie wywołań metod super jest wymagane.

Vikram
źródło
2
Łał. Dzięki za wskazówki. Zarówno to, jak i odpowiedzi @ Sherif zapewniają ważny kontekst. Jeśli któryś z was może podsumować odpowiedzi na tej stronie, oznaczyłbym ją jako zaakceptowaną. Prosimy o dołączenie: 1. Odpowiedzi na tej stronie. 2. Odpowiedź @ Philipa na tej stronie 3. Odpowiedź @ CommonsWare na tej stronie 4. Ta dyskusja Chciałbym, ale nie chcę
Anudeep Bulla
Cześć. Czy mógłbyś podsumować, widząc, że @Sherif nie chce?
Anudeep Bulla
@AnudeepBulla Cześć Anudeep, daj mi czas do jutra. Dodam odpowiedni materiał do mojej odpowiedzi i zostawię tutaj komentarz.
Vikram
@AnudeepBulla Dodałem powyżej podsumowanie. Daj mi znać, jeśli coś przegapiłem.
Vikram
@Vikram Czy TL; DR. że wywołanie onDestroyi onStopostatni jest bezpieczniejszym domyślnym, a onPausew kilku przypadkach może być trudniej? Czy mógłbyś to najpierw dodać? Kusiło mnie, aby samodzielnie edytować odpowiedź, ale nie jestem pewien, czy to podsumowanie jest poprawne.
Blaisorblade
13

Ponieważ (mówisz) warto zadzwonić do super onCreate first: pomyśl o tym.

Kiedy chcę tworzyć, Moje super tworzy swoje zasoby> Tworzę swoje zasoby.

Odwrotnie: (rodzaj stosu)

Kiedy chcę zniszczyć, niszczę swoje zasoby> Mój super niszczy jego zasoby.


W tym sensie ma zastosowanie do dowolnej pary funkcji (onCreate / onDestroy, onResume / onPause, onStart / onStop). Oczywiście onCreate utworzy zasoby, a onDestroy je zwolni. Nawiasem mówiąc, ten sam dowód dotyczy innych par.

Rozważmy pobraną bibliotekę, która ma funkcję LocationActivity zawierającą funkcję getLocation (), która zapewnia lokalizację. Najprawdopodobniej to działanie będzie musiało zainicjować swoje rzeczy w onCreate (), co zmusi cię do wywołania najpierw super.onCreate. Już to robisz, bo czujesz, że to ma sens. Teraz w swoim onDestroy decydujesz, że chcesz zapisać lokalizację gdzieś w SharedPreferences. Jeśli najpierw wywołasz super.onDestroy, jest do pewnego stopnia możliwe, że getLocation zwróci wartość null po tym wywołaniu, ponieważ implementacja LocationActivity unieważnia wartość lokalizacji w onDestroy. Chodzi o to, że nie możesz tego winić, jeśli tak się stanie. Dlatego na koniec nazwałbyś super.onDestroy, gdy skończysz z własnym onDestroy. Mam nadzieję, że to trochę sensowne.

Jeśli powyższe ma sens, weź pod uwagę, że w dowolnym momencie prowadzimy działalność, która jest zgodna z powyższą koncepcją. Jeśli chcę rozszerzyć tę aktywność, prawdopodobnie będę czuć to samo i postępować zgodnie z tym samym porządkiem z powodu tego samego dokładnego argumentu.

Przez indukcję każda czynność powinna robić to samo. Oto dobra klasa abstrakcyjna dla zajęć, które muszą przestrzegać następujących zasad:

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

Wreszcie, co się stanie, jeśli Twoja aktywność o nazwie AnudeepBullaActivityrozszerzy BaseActivity, a później chcę utworzyć, SherifElKhatibActivityktóra rozszerzy Twoją aktywność? W jakiej kolejności mam wywoływać super.dofunkcje? Ostatecznie jest to to samo.


Co do twojego pytania:

Myślę, że intencją Google jest powiedzenie nam: proszę dzwonić do super bez względu na to, gdzie. Jako ogólną praktykę nazwij to oczywiście na początku. Google ma oczywiście najzdolniejszych inżynierów i programistów, więc prawdopodobnie wykonali dobrą robotę izolując ich super wywołania i nie ingerując w rozmowy dzieci.

Trochę próbowałem i prawdopodobnie nie jest łatwo (ponieważ to Google staramy się udowodnić, że się mylimy), aby utworzyć działanie, które ulegnie awarii z powodu wywołania When is super.

Czemu?

Cokolwiek zrobione w tych funkcjach jest tak naprawdę prywatne dla klasy Activity i nigdy nie spowodowałoby konfliktu z twoją podklasą. Na przykład (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 i mManagedDialogs oraz mSearchManager są polami prywatnymi. To, co się tutaj dzieje, nie będzie miało wpływu na żaden publiczny / chroniony interfejs API.

Jednak w interfejsie API 14 dodano dispatchActivityDestroyed w celu wysłania onActivityDestroyed do ActivityLifecycleCallbacks zarejestrowanego w aplikacji. Dlatego każdy kod, który zależałby od jakiejś logiki w Twoim ActivityLifecycleCallbacks, będzie miał inny wynik w zależności od tego, kiedy dzwonisz do super. Na przykład:

Utwórz klasę aplikacji, która zlicza liczbę aktualnie uruchomionych działań:

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

Poniższe informacje mogą nie mieć sensu lub nie są dobrą praktyką, ale służą tylko do udowodnienia racji (można znaleźć bardziej realną sytuację). Utwórz MainActivity, które rzekomo przechodzi do działania GoodBye, gdy jest zakończone i kiedy jest to ostatnie działanie:

@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));
    }
}

Jeśli zadzwonisz do super.onDestroy na początku swojego onDestroy, zostanie uruchomiona aktywność GoodBye. Jeśli zadzwonisz do super.onDestroy pod koniec operacji onDestroy, działanie GoodBye nie zostanie uruchomione.

Oczywiście nie jest to optymalny przykład. To jednak pokazuje, że Google trochę tu popsuło. Żadna z pozostałych zmiennych nie wpłynęłaby na zachowanie aplikacji. Jednak dodanie tych komunikatów do onDestroy spowodowało, że super-klasa w jakiś sposób kolidowała z twoją podklasą.

Mówię, że zadzierali też z innego powodu. Nie tylko dotykali (przed api 14) tylko w super wywołaniach tego, co jest ostateczne i / lub prywatne, ale także nazywali różne funkcje wewnętrzne (prywatne), które w rzeczywistości wywoływały funkcje onPause….

Na przykład performStopfunction to funkcja o nazwie, która z kolei wywołuje funkcję 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;
}

Zauważ, że gdzieś w tej funkcji wywołują one funkcję onStop działania. Dlatego mogliby równie dobrze umieścić cały kod (zawarty w super.onStop) przed lub po wywołaniu onStop, a następnie po prostu powiadomić podklasy o onStop, używając pustych super funkcji onStop i nawet bez dodawania wyjątku SuperNotCalledException lub sprawdzania tego wywołania.

W tym celu, gdyby wywołali tę wysyłkę do ActivityLifeCycle w performDestroy, zamiast wywoływać ją na końcu super.onDestroy, zachowanie naszej aktywności byłoby takie samo niezależnie od tego, kiedy wywołaliśmy super.

W każdym razie jest to pierwsza rzecz, którą robią (trochę źle) i jest to tylko w API 14.

Sherif elKhatib
źródło
Nigdy nie zadawano sobie pytania, dlaczego wywołanie super.onDestroy () last ma sens. Uwielbiam twój przykład z biblioteki. Dokładnie to, co chciałem przekazać. Nie mogłem się bardziej zgodzić co do tego, że wywołanie super metod trwa tak długo, jak trwają pół cykli niszczenia, dokładnie jak w stosie; aby zapobiec przypadkowej utracie danych. Problem w tym, dlaczego Google nalega na pierwsze wywołanie super metod, biorąc pod uwagę powyższe założenie? Zadałem to pytanie, bo pomyślałem, że może ja i na pozór ty też podchodzę do tego zupełnie inaczej. Pozdrawiam
Anudeep Bulla
Och, nie widziałem sugestii Google i innego sposobu: p! Posłuchaj, spróbuję stworzyć działanie, które ulegnie awarii, jeśli najpierw zadzwonisz do Destroy. Powinieneś też tego spróbować. Pozdrawiam
Sherif elKhatib
@AnudeepBulla, możesz sprawdzić moje zmiany. A tak przy okazji, możesz przestać próbować. super.onprawdopodobnie nigdy nie przerwie Twojej aktywności.
Sherif elKhatib
Łał. Dzięki za wskazówki. Zarówno ta odpowiedź, jak i @ User zapewniają ważny kontekst. Jeśli któryś z was może podsumować odpowiedzi na tej stronie, oznaczyłbym ją jako zaakceptowaną. Prosimy o dołączenie: 1. Odpowiedzi na tej stronie. 2. Odpowiedź @ Philipa na tej stronie 3. Odpowiedź @ CommonsWare na tej stronie 4. Ta dyskusja Chciałbym, ale nie chcę
Anudeep Bulla
@AnudeepBulla Nie ma za co. Nie jestem pewien, jak zdecydujemy, kto opublikuje ostateczny artykuł.
Vikram
2

Mówisz, że Google sugeruje metodę 1, jednak Dianne Hackborn, znany inżynier platformy Android, sugeruje inaczej, zobacz link do forum Google .

Intuicyjnie sensowne jest wywołanie superklasy jako last podczas niszczenia instancji w metodach onPause, onStop i onDestroy , a najpierw podczas tworzenia instancji za pomocą metod onCreate, onResume i onStart .

NickT
źródło
Link do posta Dianne Hackborn jest ważny i potwierdza wzorzec.
Nick Westgate
1

Z perspektywy javy oto rozwiązanie tego zamieszania:

Dlaczego this () i super () muszą być pierwszą instrukcją w konstruktorze?

Konstruktor klasy nadrzędnej należy wywołać przed konstruktorem podklasy. Zapewni to, że jeśli wywołasz jakiekolwiek metody z klasy nadrzędnej w konstruktorze, klasa nadrzędna została już poprawnie skonfigurowana.

To, co próbujesz zrobić, przekazać argumenty do superkonstruktora, jest całkowicie legalne, po prostu musisz skonstruować te argumenty w tekście, lub przekazać je do swojego konstruktora, a następnie przekazać je do super:

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

Jeśli kompilator nie wymusił tego, możesz to zrobić:

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

To pokazuje, że tak naprawdę podpola muszą być inicjalizowane przed nadklasą! W międzyczasie wymaganie java „chroni” nas przed specjalizacją klasy przez wyspecjalizowanie tego, co argument superkonstruktor

W przypadkach, gdy klasa nadrzędna ma domyślny konstruktor, wywołanie super jest wstawiane automatycznie przez kompilator. Ponieważ każda klasa w Javie dziedziczy po Object, konstruktor obiektów musi zostać w jakiś sposób wywołany i musi zostać wykonany jako pierwszy. Umożliwia to automatyczne wstawianie super () przez kompilator. Wymuszenie super pojawienia się jako pierwszego wymusza, aby ciała konstruktora były wykonywane w odpowiedniej kolejności, która byłaby następująca: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth

(1) Sprawdzenie, że super jest pierwszą instrukcją, nie jest wystarczające, aby zapobiec temu problemowi. Na przykład, możesz umieścić "super (someMethodInSuper ());" w swoim konstruktorze. Próbuje uzyskać dostęp do metody w nadklasie przed jej skonstruowaniem, mimo że super jest pierwszą instrukcją.

(2) Wydaje się, że kompilator implementuje inne sprawdzenie, które samo w sobie jest wystarczające, aby zapobiec temu problemowi. Komunikat brzmi „nie można odwoływać się do xxx przed wywołaniem konstruktora typu nadrzędnego”. Dlatego sprawdzanie, czy super jest pierwszą instrukcją, nie jest konieczne

Przejdź przez ten http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html

LOG_TAG
źródło
Doskonale rozumiem, co tam publikujesz. Najpierw wywołujesz konstruktory superklasy, ponieważ mogą zainicjować zasoby, których dziecko może potrzebować; a destruktory trwają, bo prawdopodobnie nie chcesz wymazać wszystkich rodziców z lokalnych zasobów, czyniąc je bezcelowymi. Dokładnie o to mi chodzi. A ponieważ onPause, onStop i onDestroy mają za zadanie zapisywanie informacji o stanie i mniej więcej udostępnianie zasobów do GC (czyli w pewnym sensie ich niszczenie), uważam je za analogiczne do destruktorów i dlatego uważam, że nazwanie ich ostatnimi ma sens. Nie?
Anudeep Bulla
Wszystko, co powiedziałeś powyżej, jest dokładnie tym, co miałem na myśli, kiedy powiedziałem: „wywoływanie super metod ma sens w połowie cyklu tworzenia”. Jestem zaniepokojony i zdezorientowany w przypadku metod półcyklu niszczenia, które wydają się bardziej analogiczne do destruktorów. Pozdrawiam
Anudeep Bulla
1

Najważniejszą rzeczą, o której należy pamiętać, jest to, że super.onPause()niejawnie wywołuje setResult(Activity.RESULT_CANCELED). Ale setResultmożna zadzwonić tylko raz, a wszystkie kolejne połączenia są ignorowane. Więc jeśli chcesz odepchnąć jakikolwiek wynik z powrotem do czynności rodzicielskiej, musisz zadzwonić do setResultsiebie, zanim zadzwonisz super.onPause(). O ile wiem, to największa pułapka.

Philip Sheard
źródło
Wow, to wydaje się ważne. Jest więc sytuacja, w której zdecydowanie musisz opóźnić wywołanie super metod. Dziękuję Ci.
Anudeep Bulla
super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED). Czy możesz powiedzieć, skąd to masz?
Vikram,
Byłem zmieszany. W rzeczywistości to finish () wywołuje setResult, a super.onBackPressed () wywołuje finish (). Tak więc setResult zdecydowanie musi zostać wywołany przed super.onBackPressed (). Nie jestem pewien, czy są jakieś okoliczności, w których super.onPause () mogłoby spowodować wywołanie setResult, ale wolę nie ryzykować.
Philip Sheard
1

OBA są poprawne IMO

Według docs

Klasy pochodne muszą wywoływać implementację tej metody w superklasie. Jeśli tego nie zrobią, zostanie zgłoszony wyjątek.

Super metoda powinna być zawsze wywoływana, gdy dokumentacja wyraźnie to mówi.

Możesz jednak wybrać, kiedy wywołać metodę super.

Patrząc na źródło onPause

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

Dlatego nie ma znaczenia przed lub po jego wywołaniu. Powinnaś być dobra.

Ale dla najlepszej praktyki powinieneś zadzwonić najpierw.

Polecam go głównie jako mechanizm ochrony: jeśli jest wyjątek, supermetoda instancji zostanie już wywołana.

Również umieszczenie tych wywołań w pierwszej linii pomoże Ci uniknąć popełnienia błędów w przyszłości, takich jak usunięcie kodu w metodzie i przypadkowe usunięcie wywołania superklasy.

Sunil Mishra
źródło
Przepraszam, jeśli za pierwszym razem pytanie nie było do końca jasne, ale uprzejmie spójrz na to teraz.
Anudeep Bulla
@AnudeepBulla To właśnie wam wyjaśniłem. Możesz użyć dowolnego z nich. Oba są ważne.
Sunil Mishra,
Rozumiem, że niestandardowa implementacja metod onPause, onStop i onDestroy nie jest bezwzględnie konieczna. Stworzyłem wiele aplikacji bez nich. Więc co masz na myśli mówiąc, że metody Super powinny być zawsze nazywane? Są wywoływane niejawnie, nawet bez nadpisywania. Wiem też, że oba działają. Chcę wiedzieć, dlaczego doktorzy mówią, że należy najpierw zadzwonić do super. A jeśli to pytanie nadal nie jest oczywiste, czy możesz wyjaśnić DLACZEGO, kiedy mówisz „Ale dla najlepszej praktyki powinieneś najpierw do tego zadzwonić”?
Anudeep Bulla
Jeśli tego nie zmienisz, metody są wywoływane z Base Class ale jeśli zastąpisz, wymagane jest, aby zadzwonić do superinnego, który będziesz miećandroid.app.SuperNotCalledException
Sunil Mishra
1
Wydajesz się źle rozumieć. Nie chodzi o to, czy zadzwonić, czy nie. Jak zauważyłeś, MUSISZ to zrobić, jeśli je zastąpisz. Pytanie brzmi, kiedy?
Anudeep Bulla
0

Super wywołania zwrotne są potrzebne, aby wewnętrznie ustawić Aktywność we właściwym stanie dla systemu.

Załóżmy, że zaczynasz swoją aktywność, a onCreate jest wywoływane przez system. Teraz możesz go nadpisać i np. Załadować swój układ. Ale ze względu na przepływ systemu musisz zadzwonić do super, aby system mógł kontynuować standardową procedurę. Dlatego wyjątek zostanie wyrzucony, jeśli go nie nazwiesz.

Dzieje się to niezależnie od implementacji w onCreate. To jest tylko importowane dla systemu. Gdyby nie było ANR, mógłbyś mieć nieskończoną pętlę w każdym wywołaniu zwrotnym i działanie zostałoby złapane w tym. W ten sposób system wie, kiedy połączenie zwrotne zostało zakończone i następnie wywołuje następny.

Znam tylko jedną sytuację, w której konieczne jest wykonanie super rozmowy. Jeśli chcesz zmienić standardowe zachowanie motywu lub wyświetlacza itp. W onCreate, musisz to zrobić, zanim zadzwonisz do super, aby zobaczyć efekt. W przeciwnym razie AFAIK nie ma różnicy, w którym momencie to nazwiesz.

Ale aby pozwolić systemowi zrobić to, co może najlepiej, umieść super w pierwszej linii wywołania zwrotnego, po którym następuje kod, jeśli nie masz dobrego powodu, aby z nim zerwać.

Steve Benett
źródło
Sekwencja w onCreate wydaje się całkiem zrozumiała. Co dzieje się w metodach niszczenia? Powiedz onStop. Załóżmy, że moja implementacja onStop wykorzystuje część zasobów, które zwalnia metoda super, jeśli jest wywoływana. W takim przypadku sensowne jest wywołanie metody super po mojej implementacji.
Anudeep Bulla
Idealnie, tak powinno być, prawda. Nasza działalność zawsze będzie miała zasoby, które ma superklasa, a nawet więcej, a te, które są niezależne od mojej działalności, mogą w większości przypadków zależeć od powszechnych zasobów nadklasy. Bardziej sensowne jest najpierw zajęcie się moimi zasobami, a następnie wezwanie superklasy do zajęcia się zwykłymi. Dlaczego więc Google mówi, że „POWINIENEŚ” najpierw wywoływać metody nadklasy?
Anudeep Bulla
O jakim zasobie mówisz, do którego możesz uzyskać dostęp w onCreate, ale nie w onDestroy?
Steve Benett
Nie mam przypadku użycia. Zastanawiam się tylko, jak to się różni w stylu OOP. Konstruktory superklas są wywoływane jako pierwsze, przed implementacją, a destruktory superklas są wywoływane jako ostatnie, po implementacji, prawda? onPause, onStop i onDestroy nie są wyłącznie destruktorami, ale zwykle robią to samo, prawda? Co najmniej onDestroy i głównie onStop też .. nie?
Anudeep Bulla
Zobacz wywołania zwrotne w sposób zmieniający stan, a nie jako konstruktor / destruktor. Każde wywołanie zwrotne ma swoją potrzebę, np. Utworzyć (gotowe do użycia) / zniszczyć (ostatnia szansa na interakcję) Aktywność lub umieścić ją na pierwszym planie / w tle. Wywołania zwrotne umożliwiają kontrolę zasobów w przepływie systemu. System po prostu sprawdza, w jakim jest stanie i odpowiednio obsługuje. Zasoby, z których korzystasz, i te, którymi steruje system, są od siebie niezależne i nie będzie żadnego przecięcia.
Steve Benett,