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();
}
źródło
Odpowiedzi:
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:
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()"); }
mCalled
jest ustawiony na true wActivity.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 first
wydaje się nie mieć zbyt wielu dowodów na poparcie tego. Co gorsza (dla oświadczenia) jest to, że poniższy kod zaczerpnięto zandroid.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łaniastartActivityForResult(Intent)
. Ustawienie wyniku za pomocąsetResult(...)
aftersuper.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:
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 :
Bob Kerns z tego wątku :
Użytkownik Steve Benett również zwraca uwagę na to:
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 first
ma 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.źródło
onDestroy
ionStop
ostatni jest bezpieczniejszym domyślnym, aonPause
w 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.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
AnudeepBullaActivity
rozszerzy BaseActivity, a później chcę utworzyć,SherifElKhatibActivity
która rozszerzy Twoją aktywność? W jakiej kolejności mam wywoływaćsuper.do
funkcje? 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
performStop
function 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.
źródło
super.on
prawdopodobnie nigdy nie przerwie Twojej aktywności.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 .
źródło
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
źródło
Najważniejszą rzeczą, o której należy pamiętać, jest to, że
super.onPause()
niejawnie wywołujesetResult(Activity.RESULT_CANCELED)
. AlesetResult
moż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ć dosetResult
siebie, zanim zadzwoniszsuper.onPause()
. O ile wiem, to największa pułapka.źródło
super.onPause() implicitly calls setResult(Activity.RESULT_CANCELED)
. Czy możesz powiedzieć, skąd to masz?OBA są poprawne IMO
Według docs
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,
super
metoda 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.
źródło
Base Class
ale jeśli zastąpisz, wymagane jest, aby zadzwonić dosuper
innego, który będziesz miećandroid.app.SuperNotCalledException
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ć.
źródło