java - savedinstancestate - ¿Cuál es el orden correcto de llamar a los métodos de superclase en los métodos onPause, onStop y onDestroy? ¿y por qué?
onstart android example (7)
La destrucción de los recursos específicos de la instancia primero, antes de destruir los recursos de superclase de los que dependen los recursos específicos de la instancia, tiene sentido, y no al revés. Pero los comentarios sugieren lo contrario. ¿Qué me estoy perdiendo?
En mi opinión: ni una sola cosa.
Esta respuesta de Mark (también conocido como CommonsWare on SO) arroja luz sobre el problema: Link - ¿Debería la llamada al método de la superclase ser la primera declaración? . Pero luego, puede ver el siguiente comentario en su respuesta:
Pero ¿por qué el documento oficial dice: "Siempre llame primero al método de superclase" en onPause ()?
Volver al punto de partida. De acuerdo, veamos esto desde otro ángulo. Sabemos que Java Language Specification no especifica un orden en el que se debe realizar la llamada a super.overridenMethod()
(o si la llamada se debe colocar en absoluto).
En el caso de la actividad de la clase, se requieren y aplican las llamadas a super.overridenMethod()
:
if (!mCalled) {
throw new SuperNotCalledException(
"Activity " + mComponent.toShortString() +
" did not call through to super.onStop()");
}
mCalled
se establece en verdadero en Activity.onStop()
.
Ahora, el único detalle que queda para debatir es el orden.
I also know that both work
Por supuesto. Mire el cuerpo del método para 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;
}
De cualquier forma que super.onPause()
la llamada a super.onPause()
, estarás bien. Activity.onStop () tiene un cuerpo de método similar. Pero eche un vistazo a 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);
}
Aquí, el orden podría importar dependiendo de cómo se configura su actividad, y si llamar a super.onDestroy()
interferiría con el código que sigue.
Como última palabra, la afirmación Always call the superclass method first
no parece tener mucha evidencia para respaldarlo. Lo que es peor (para la declaración) es que el siguiente código se ha tomado de android.app.ListActivity
:
public class ListActivity extends Activity {
....
@Override
protected void onDestroy() {
mHandler.removeCallbacks(mRequestFocus);
super.onDestroy();
}
....
}
Y, a partir de la aplicación de ejemplo LunarLander incluida en Android SDK:
public class LunarLander extends Activity {
....
@Override
protected void onPause() {
mLunarView.getThread().pause(); // pause game when Activity pauses
super.onPause();
}
....
}
Resumen y menciones dignas:
Usuario Philip Sheard : Proporciona un escenario donde una llamada a super.onPause()
debe retrasarse en caso de que una actividad se inicie usando startActivityForResult(Intent)
. Establecer el resultado usando setResult(...)
después de super.onPause()
no funcionará. Luego aclara esto en los comentarios a su respuesta.
Usuario Sherif elKhatib : Explica por qué dejar que la superclase inicialice sus recursos primero y destruya sus recursos de la lógica a continuación:
Consideremos una biblioteca que descargó que tiene una actividad de ubicación que contiene una función getLocation () que proporciona la ubicación. Lo más probable es que esta actividad deba inicializar sus cosas en onCreate () lo que lo forzará a llamar primero a super.onCreate . Ya lo haces porque sientes que tiene sentido. Ahora, en su onDestroy, decide que desea guardar la ubicación en algún lugar de SharedPreferences. Si llama primero a super.onDestroy, en cierta medida es posible que getLocation devuelva un valor nulo después de esta llamada porque la implementación de LocationActivity anula el valor de ubicación en onDestroy. La idea es que no lo culparías si esto sucediera. Por lo tanto, llamaría a super.onDestroy al final después de que haya terminado con su propio onDestroy.
Continúa super.X()
: si una clase secundaria está adecuadamente aislada (en términos de dependencia de recursos) de la clase principal, las llamadas super.X()
no necesitan cumplir con ninguna especificación de orden.
Consulte su respuesta en esta página para leer un escenario donde la ubicación de la llamada a super.onDestroy()
afecta la lógica del programa.
De una respuesta de Marcos :
Métodos que reemplaza que son parte de la creación de componentes (onCreate (), onStart (), onResume (), etc.), debe encadenar a la superclase como primera instrucción , para asegurarse de que Android tenga la oportunidad de hacer su trabajo antes que usted intentar hacer algo que dependa de que se haya hecho ese trabajo.
Métodos que anula que son parte de la destrucción de componentes (onPause (), onStop (), onDestroy (), etc.), debe hacer su trabajo primero y encadenar a la superclase como la última opción . De esta forma, en caso de que Android limpie algo de lo que depende tu trabajo, primero habrás hecho tu trabajo.
Métodos que devuelven algo distinto de void (onCreateOptionsMenu (), etc.), a veces encadena a la superclase en la declaración de devolución, suponiendo que no está haciendo específicamente algo que necesita forzar un determinado valor de retorno.
Todo lo demás, como onActivityResult (), depende de usted, en general. Tiendo a encadenarme a la superclase como la primera cosa, pero a menos que tengas problemas, encadenar más adelante debería estar bien.
Bob Kerns de este hilo :
Es un buen patrón [(el patrón que Mark sugiere más arriba)], pero he encontrado algunas excepciones. Por ejemplo, el tema que quería aplicar a mi PreferenceActivity no tendría efecto a menos que lo coloque antes de onCreate () de la superclase.
El usuario Steve Benett también llama la atención sobre esto:
Solo conozco una situación en la que el momento de la súper llamada es necesario. Si desea alterar el comportamiento estándar del tema o la pantalla y tal como en Crear, debe hacerlo antes de llamar a super para ver un efecto . De lo contrario, AFAIK no hay diferencia en qué momento lo llamas.
El usuario Sunil Mishra confirma que el orden (lo más probable) no juega un papel cuando se llaman métodos de clase de actividad. También afirma que llamar primero a los métodos de superclase se considera una mejor práctica . Sin embargo, no pude corroborar esto.
Usuario LOG_TAG : Explica por qué una llamada al constructor de superclase debe ser antes de todo lo demás. En mi opinión, esta explicación no se agrega a la pregunta que se hace.
Nota final : Confíe, pero verifique. La mayoría de las respuestas en esta página siguen este enfoque para ver si el enunciado Always call the superclass method first
tiene Always call the superclass method first
respaldo lógico. Como resultado, no es así; al menos, no en el caso de la actividad de la clase. En general, uno debe leer el código fuente de la superclase para determinar si el pedido de llamadas a los métodos de Super es un requisito.
Estaba yendo a través del sitio para desarrolladores de Android, actualizando el ciclo de actividad de la actividad, y en cada ejemplo de código, hay un comentario al lado de los métodos de clase superior que dice "Siempre llame primero al método de superclase".
Aunque esto tiene sentido en el ciclo medio de creación: onCreate, onStart y onResume, estoy un poco confundido en cuanto a cuál es el procedimiento correcto en el medio ciclo de destrucción: onPause, onStop, onDestroy.
La destrucción de los recursos específicos de la instancia primero, antes de destruir los recursos de superclase de los que dependen los recursos específicos de la instancia, tiene sentido, y no al revés. Pero los comentarios sugieren lo contrario. ¿Qué me estoy perdiendo?
Editar : Dado que las personas parecen confundirse en cuanto a la intención de la pregunta, lo que quiero saber es cuál de las siguientes opciones es la correcta. Y POR QUÉ ?
1.Google sugiere
@Override
protected void onStop() {
super.onStop(); // Always call the superclass method first
//my implementation here
}
2. La otra manera
@Override
protected void onStop() {
//my implementation here
super.onStop();
}
Dado que (dices) tiene sentido llamar a super enCrear primero: Piénsalo.
Cuando quiero crear, My super crea sus recursos> Creo mis recursos.
Inversamente: (una especie de pila)
Cuando quiero destruir, destruyo mis recursos> Mi súper destruye sus recursos.
En este sentido, se aplica a cualquier par de funciones (onCreate / onDestroy, onResume / onPause, onStart / onStop). Naturalmente, onCreate creará recursos y onDestroy liberará estos recursos. Por cierto, la misma prueba se aplica a las otras parejas.
Consideremos una biblioteca que descargó que tiene una actividad de ubicación que contiene una función getLocation () que proporciona la ubicación. Lo más probable es que esta actividad deba inicializar sus cosas en onCreate () lo que lo forzará a llamar primero a super.onCreate. Ya lo haces porque sientes que tiene sentido. Ahora, en su onDestroy, decide que desea guardar la ubicación en algún lugar de SharedPreferences. Si llama primero a super.onDestroy, en cierta medida es posible que getLocation devuelva un valor nulo después de esta llamada porque la implementación de LocationActivity anula el valor de ubicación en onDestroy. La idea es que no lo culparías si esto sucediera. Por lo tanto, llamaría a super.onDestroy al final después de que haya terminado con su propio onDestroy. Espero que esto tenga un poco de sentido.
Si lo anterior tiene sentido, considere que en cualquier momento tenemos una actividad que se rige por el concepto anterior. Si quiero extender esta actividad, probablemente me sienta de la misma manera y sigo el mismo orden debido al mismo argumento exacto.
Por inducción, cualquier actividad debería hacer lo mismo. Aquí hay una buena clase abstracta para una actividad obligada a seguir estas reglas:
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);
}
}
Finalmente, ¿qué AnudeepBullaActivity
si su actividad llamada AnudeepBullaActivity
extiende BaseActivity y más adelante, quiero crear SherifElKhatibActivity
que amplíe su actividad? ¿En qué orden debo llamar a las funciones de super.do
? En definitiva, es lo mismo.
En cuanto a tu pregunta:
Creo que la intención de Google es decirnos: llame al súper sin importar dónde. Como práctica general, por supuesto, llámalo al principio. Google por supuesto tiene los ingenieros y desarrolladores más brillantes, por lo que probablemente hicieron un buen trabajo al aislar sus súper llamadas y no interferir en las llamadas de los niños.
Intenté un poco y probablemente no sea fácil (ya que Google intentamos probar que está mal) para crear una actividad que se bloquee fácilmente debido a cuándo se llama súper.
¿Por qué?
Cualquier cosa realizada en estas funciones es realmente privada para la clase Activity y nunca causaría ningún conflicto con su subclase. Por ejemplo (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 y mManagedDialogs y mSearchManager son todos campos privados. Y ninguna de las API públicas / protegidas se verá afectada por lo que se haga aquí.
Sin embargo, en API 14, se agregó dispatchActivityDestroyed para enviar un onActivityDestroyed a ActivityLifecycleCallbacks registrado en su aplicación. Por lo tanto, cualquier código que dependería de alguna lógica en su ActivityLifecycleCallbacks tendrá un resultado diferente en función de cuándo llame al súper. Por ejemplo:
Cree una clase de aplicación que cuente la cantidad de actividades que se están ejecutando actualmente:
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) {}
}
Lo siguiente puede no tener sentido o no es una buena práctica, pero es solo para demostrar un punto (Uno podría encontrar una situación más real). Cree la actividad principal que supuestamente va a la actividad GoodBye cuando está terminada y cuando es la última actividad:
@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 llama a super.onDestroy al comienzo de su onDestroy, se iniciará la actividad GoodBye. Si llama a super.onDestroy al final de su onDestroy, la actividad GoodBye no se iniciará.
Por supuesto, una vez más, este no es el mejor ejemplo. Sin embargo, esto muestra que Google cometió un error aquí. Cualquiera de las otras variables no habría afectado el comportamiento de su aplicación. Sin embargo, agregar este envío al onDestroy hizo que el super interfiera de alguna manera con su subclase.
Yo digo que también se equivocaron por una razón diferente. No solo ellos (antes de la api 14) solo tocaban en las super llamadas lo que era final y / o privado, sino que también llamaban a diferentes funciones internas (privadas) que realmente distribuían las funciones onPause ...
Por ejemplo, la función performStop
es la función llamada que a su vez llama a la función 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;
}
Observe que llaman a la Actividad onStop en algún lugar de esta función. Por lo tanto, también podrían poner todo el código (incluido en super.onStop) antes o después de la llamada a onStop y luego simplemente notificar subclases sobre el onStop utilizando las superfunciones vacías de OnStop y sin siquiera agregar SuperNotCalledException ni verificar que se haya llamado.
Para esto, si llamaron a este despacho al ActivityLifeCycle en el performDestroy en lugar de llamarlo al final de super.onDestroy, el comportamiento de nuestra actividad habría sido el mismo independientemente de cuándo llamamos al súper.
De todos modos, esto es lo primero que hacen (un poco mal) y solo está en la API 14.
Desde la perspectiva de Java aquí hay alguna solución para esta confusión:
¿Por qué esto () y super () tienen que ser la primera declaración en un constructor?
El constructor de la clase padre debe llamarse antes que el constructor de la subclase. Esto asegurará que si llama a cualquier método en la clase padre en su constructor, la clase padre ya se ha configurado correctamente.
Lo que estás tratando de hacer, pasar args al super constructor es perfectamente legal, solo necesitas construir esos args en línea como lo estás haciendo, o pasarlos a tu constructor y luego pasarlos a super:
public MySubClassB extends MyClass {
public MySubClassB(Object[] myArray) {
super(myArray);
}
}
Si el compilador no hizo cumplir esto, podría hacer esto:
public MySubClassB extends MyClass {
public MySubClassB(Object[] myArray) {
someMethodOnSuper(); //ERROR super not yet constructed
super(myArray);
}
}
¡Demuestra que, en realidad, los subcampos deben ser inilializados antes del supremove! Mientras tanto, el requisito de Java "nos defiende" de la especialización de la clase al especializar el argumento del superconstructor
En los casos en que una clase principal tiene un constructor predeterminado, el compilador automáticamente inserta la llamada a super. Como cada clase en Java hereda de Object, el constructor de objetos debe ser llamado de alguna manera y debe ejecutarse primero. La inserción automática de super () por el compilador lo permite. La aplicación de super para que aparezca primero, exige que los cuerpos de constructor se ejecuten en el orden correcto que sería: Object -> Parent -> Child -> ChildOfChild -> SoOnSoForth
(1) Verificar que la súper es la primera afirmación no es suficiente para evitar ese problema. Por ejemplo, podría poner "super (someMethodInSuper ());" en tu constructor Esto intenta acceder a un método en la superclase antes de que se construya, aunque super es la primera instrucción.
(2) El compilador parece implementar una verificación diferente que, por sí misma, es suficiente para evitar este problema. El mensaje es "no se puede hacer referencia a xxx antes de que se haya llamado al constructor de supertipo". Por lo tanto, no es necesario verificar esa superposición.
Por favor http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html este http://valjok.blogspot.in/2012/09/super-constructor-must-be-first.html
El super de las devoluciones de llamada es necesario para poner la Actividad en el estado correcto internamente para el sistema.
Supongamos que inicia su actividad y onCreate es invocado por el sistema. Ahora puede anularlo y, por ejemplo, cargar su diseño. Pero en aras del flujo del sistema debe llamar a super, que el sistema puede continuar con el procedimiento estándar. Es por eso que se lanzará una excepción si no la llamas.
Esto sucede independientemente de su implementación en onCreate. Es solo importante y para el sistema. Si no hubiera ANR, podría tener un bucle infinito en cualquier devolución de llamada y la actividad quedaría atrapada en ese. Entonces, el sistema sabe cuándo se ha terminado la devolución de llamada y luego llama al siguiente.
Solo conozco una situación en la que el momento de la súper llamada es necesario. Si desea alterar el comportamiento estándar del tema o la pantalla y tal como en Crear, debe hacerlo antes de llamar a super para ver un efecto. De lo contrario, AFAIK no hay diferencia en qué momento lo llamas.
Pero para que el sistema haga lo que mejor pueda hacer es colocar el súper en la primera línea de una devolución de llamada seguido de su código, si no tiene una buena razón para romperlo.
Lo más importante a tener en cuenta es que super.onPause()
llama implícitamente a setResult(Activity.RESULT_CANCELED)
. Pero setResult
solo se puede llamar una vez, y todas las llamadas subsiguientes se ignoran. Por lo tanto, si desea devolver cualquier tipo de resultado a la actividad principal, debe llamar a setResult
usted mismo, antes de llamar a super.onPause()
. Esa es la mayor sorpresa, hasta donde yo sé.
Usted dice que Google sugiere el método 1, sin embargo, Dianne Hackborn, un conocido ingeniero de framework de Android, sugiere lo contrario, consulte el enlace al foro de Google .
Tiene sentido intuitivo llamar a la superclase al último cuando destruye una instancia en los métodos onPause, onStop y onDestroy y, en primer lugar, al crear una instancia con los métodos de onCreate, onResume y onStart .
AMBOS son correctos IMO
De acuerdo con los documentos
Las clases derivadas deben llamar a la implementación de este método de la superclase. Si no lo hacen, se lanzará una excepción.
Siempre se debe llamar al método Super
cuando la documentación lo indique explícitamente.
Sin embargo, puede elegir cuándo llamar al súper método.
Mirando la fuente de onPause
protected void onPause() {
getApplication().dispatchActivityPaused(this);
mCalled = true;
}
Por lo tanto, no importa antes o después de que se llame. Deberías ser bueno.
Pero para la mejor práctica, debería llamarlo primero.
Lo recomiendo sobre todo como un mecanismo de protección: si hay una excepción, entonces ya se habrá llamado al método de la superinstancia.
Además, colocar estas llamadas en la primera línea lo ayudará a evitar cometer errores en el futuro, como eliminar código en el método y borrar accidentalmente la llamada a la súper clase.