vida savedinstancestate onrestart example ciclo activity android android-lifecycle activity-lifecycle

savedinstancestate - onstart android example



Al cerrar sesión, desactive la pila del historial de actividades, evitando que el botón "atrás" abra las actividades de inicio de sesión solo (18)

Todas las actividades en mi aplicación requieren que un usuario inicie sesión para ver. Los usuarios pueden desconectarse de casi cualquier actividad. Este es un requisito de la aplicación. En cualquier momento, si el usuario cierra la sesión, deseo enviar al usuario a la Activity inicio de sesión. En este punto, quiero que esta actividad se encuentre en la parte inferior de la pila del historial, de modo que al presionar el botón "atrás" el usuario regrese a la pantalla de inicio de Android.

He visto esta pregunta en varios lugares diferentes, todos con respuestas similares (que describo aquí), pero quiero plantearla aquí para recopilar comentarios.

He intentado abrir la actividad de inicio de sesión estableciendo sus indicadores de Intent en FLAG_ACTIVITY_CLEAR_TOP que parece funcionar como se describe en la documentación, pero no logro mi objetivo de ubicar la actividad de inicio de sesión en la parte inferior de la pila de historial e impedir que el usuario navegando de nuevo a las actividades iniciadas anteriormente. También intenté usar android:launchMode="singleTop" para la actividad de inicio de sesión en el manifiesto, pero esto tampoco cumple mi objetivo (y parece que no tiene ningún efecto).

Creo que necesito borrar el montón de historial o terminar todas las actividades abiertas previamente.

Una opción es hacer que cada actividad onCreate el onCreate de onCreate sesión y finish() si no ha iniciado sesión. No me gusta esta opción, ya que el botón Atrás seguirá estando disponible para su uso, navegando hacia atrás a medida que se cierran las actividades.

La siguiente opción es mantener una lista LinkedList de referencias a todas las actividades abiertas a las que se puede acceder estáticamente desde cualquier lugar (quizás utilizando referencias débiles). Al cerrar sesión, accederé a esta lista e iteraré sobre todas las actividades abiertas previamente, invocando finish() en cada una. Probablemente comenzaré a implementar este método pronto.

Sin embargo, prefiero usar algunos trucos de la bandera Intent para lograr esto. Me encantaría descubrir que puedo cumplir con los requisitos de mi solicitud sin tener que usar ninguno de los dos métodos que he descrito anteriormente.

¿Hay alguna forma de lograr esto utilizando la configuración de Intent o manifiesto, o es mi segunda opción, mantener una LinkedList de actividades abiertas en LinkedList la mejor opción? ¿O hay otra opción que estoy pasando por alto?


Una opción es hacer que cada actividad verifique el estado de inicio de sesión y finalice () si no ha iniciado sesión. No me gusta esta opción, ya que el botón Atrás seguirá estando disponible para su uso, navegando hacia atrás a medida que se cierran las actividades.

Lo que quiere hacer es llamar a logout () y terminar () en sus métodos onStop () o onPause (). Esto obligará a Android a llamar a onCreate () cuando la actividad vuelva a activarse, ya que no la tendrá en la pila de su actividad por más tiempo. Luego, haga lo que dice, en onCreate () verifique el estado de inicio de sesión y reenvíelo a la pantalla de inicio de sesión si no ha iniciado sesión.

Otra cosa que podría hacer es verificar el estado de inicio de sesión en onResume () y, si no ha iniciado sesión, finalizar () e iniciar la actividad de inicio de sesión.


Al hacer clic en Logout puede llamar a esto

private void GoToPreviousActivity() { setResult(REQUEST_CODE_LOGOUT); this.finish(); }

onActivityResult () de la Actividad anterior, vuelve a llamar a este código anterior hasta que hayas terminado todas las actividades.


Alguna vez finish() no funciona

He resuelto ese problema con

finishAffinity()


Aquí está la solución que se me ocurrió en mi aplicación.

En mi LoginActivity, después de procesar con éxito un inicio de sesión, comienzo el siguiente de manera diferente según el nivel de API.

Intent i = new Intent(this, MainActivity.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { startActivity(i); finish(); } else { startActivityForResult(i, REQUEST_LOGIN_GINGERBREAD); }

Luego, en el método onActivityForResult de mi LoginActivity:

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB && requestCode == REQUEST_LOGIN_GINGERBREAD && resultCode == Activity.RESULT_CANCELED) { moveTaskToBack(true); }

Finalmente, después de procesar un cierre de sesión en cualquier otra actividad:

Intent i = new Intent(this, LoginActivity.class); i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(i);

Cuando está en Gingerbread, lo hace así que si presiono el botón Atrás de MainActivity, la actividad de inicio de sesión se oculta inmediatamente. En Honeycomb y más tarde, acabo de iniciar la actividad de inicio de sesión después de procesar un inicio de sesión y se recrea correctamente después de procesar un cierre de sesión.


Comience su actividad con StartActivityForResult y mientras cierra la sesión, establezca su resultado y de acuerdo con su resultado, finalice su actividad

intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); startActivityForResult(intent, BACK_SCREEN); @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case BACK_SCREEN: if (resultCode == REFRESH) { setResult(REFRESH); finish(); } break; } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { AlertDialog.Builder builder = new AlertDialog.Builder(this); AlertDialog alertDialog = builder.create(); alertDialog .setTitle((String) getResources().getText(R.string.home)); alertDialog.setMessage((String) getResources().getText( R.string.gotoHome)); alertDialog.setButton(DialogInterface.BUTTON_POSITIVE, "Yes", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { setResult(REFRESH); finish(); } }); alertDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "No", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }); alertDialog.show(); return true; } else return super.onKeyDown(keyCode, event); }


Es posible administrando una marca en SharedPreferences o en Application Activity.

Al iniciar la aplicación (en la Pantalla de inicio), establezca el indicador = falso; El evento On Logout Click simplemente establece el indicador true y en OnResume () de cada actividad, verifique si el indicador es verdadero y luego llame a finish ().

Funciona a las mil maravillas :)


Esto funcionó para mí:

// After logout redirect user to Loing Activity Intent i = new Intent(_context, MainActivity.class); // Closing all the Activities i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); // Add new Flag to start new Activity i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); // Staring Login Activity _context.startActivity(i);


La respuesta seleccionada es inteligente y complicada. Así es como lo hice:

LoginActivity es la actividad raíz de la tarea, establezca android: noHistory = "true" en Manifest.xml; Digamos que quiere cerrar sesión en SettingsActivity, puede hacerlo de la siguiente manera:

Intent i = new Intent(SettingsActivity.this, LoginActivity.class); i.addFlags(IntentCompat.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(i);


La solución aceptada no es correcta, tiene problemas, ya que usar un receptor de difusión no es una buena idea para este problema. Si su actividad ya ha llamado al método onDestroy (), no obtendrá el receptor. La mejor solución es tener un valor booleano en sus preferencias compartidas y verificarlo en el método onCreate () de su actividad. Si no se debe llamar cuando el usuario no ha iniciado sesión, finalice la actividad. Aquí está el código de ejemplo para eso. Tan simple y funciona para cada condición.

protected void onResume() { super.onResume(); if (isAuthRequired()) { checkAuthStatus(); } } private void checkAuthStatus() { //check your shared pref value for login in this method if (checkIfSharedPrefLoginValueIsTrue()) { finish(); } } boolean isAuthRequired() { return true; }


La solución que proporcionó @doreamon funciona bien para todos los casos excepto uno:

Si después de iniciar sesión, el usuario de la pantalla de inicio de sesión de Killing navegó directamente a una pantalla intermedia. Por ejemplo, en un flujo de A-> B-> C, navegue como: Iniciar sesión -> B -> C -> Presione el acceso directo a la página de inicio. El uso de FLAG_ACTIVITY_CLEAR_TOP borra solo la actividad C, ya que el Hogar (A) no está en el historial de la pila. Presionar Back en la pantalla A nos llevará de nuevo a B.

Para abordar este problema, podemos mantener una pila de actividades (Arraylist) y cuando se presiona el inicio, tenemos que eliminar todas las actividades en esta pila.


Muchas respuestas. Puede que este también te ayude.

Intent intent = new Intent(activity, SignInActivity.class) .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); this.startActivity(intent); this.finish();


Parece un rito de paso que un nuevo programador de Android pase un día investigando este problema y leyendo todos estos hilos de . Ahora estoy recién iniciado y dejo aquí el rastro de mi humilde experiencia para ayudar a un futuro peregrino.

Primero, no hay una forma obvia o inmediata de hacer esto según mi investigación (as of September 2012). Usted pensaría que podría startActivity(new Intent(this, LoginActivity.class), CLEAR_STACK) pero no .

PUEDE hacer startActivity(new Intent(this, LoginActivity.class)) con FLAG_ACTIVITY_CLEAR_TOP - y esto hará que el marco busque la pila, encuentre su instancia original anterior de LoginActivity, vuelva a crearla y elimine el resto de la pila (hacia arriba) . Y dado que el inicio de sesión está probablemente en la parte inferior de la pila, ahora tiene una pila vacía y el botón Atrás acaba de salir de la aplicación.

PERO, esto solo funciona si previamente dejaste viva la instancia original de LoginActivity en la base de tu pila. Si, como muchos programadores, LoginActivity finish() esa actividad de LoginActivity una vez que el usuario ha iniciado sesión correctamente, ya no FLAG_ACTIVITY_CLEAR_TOP en la base de la pila y la semántica de FLAG_ACTIVITY_CLEAR_TOP no se aplicará ... terminas creando una nueva LoginActivity de LoginActivity de LoginActivity en parte superior de la pila existente. Es casi seguro que NO es lo que quieres (comportamiento extraño en el que el usuario puede "retroceder" para salir de la sesión en una pantalla anterior).

Por lo tanto, si ha finish() la actividad de LoginActivity , debe buscar algún mecanismo para limpiar su pila y luego iniciar una nueva LoginActivity inicio de LoginActivity . Parece que la respuesta de @doreamon en este hilo es la mejor solución (al menos para mi humilde ojo):

https://.com/a/9580057/614880

Sospecho fuertemente que las complicadas implicaciones de si dejas la actividad de inicio de sesión con vida están causando gran parte de esta confusión.

Buena suerte.


Pasé algunas horas en esto también ... y estoy de acuerdo en que FLAG_ACTIVITY_CLEAR_TOP suena como lo que quieres: borra toda la pila, excepto la actividad que se inicia, por lo que el botón Atrás sale de la aplicación. Sin embargo, como mencionó , FLAG_ACTIVITY_CLEAR_TOP solo funciona cuando la actividad que estás iniciando ya está en la pila; Cuando la actividad no está ahí, la bandera no hace nada.

¿Qué hacer? Coloque la actividad que se está iniciando en la pila con FLAG_ACTIVITY_NEW_TASK , lo que hace que esa actividad sea el inicio de una nueva tarea en la pila de historial. A continuación, agregue el indicador FLAG_ACTIVITY_CLEAR_TOP.

Ahora, cuando FLAG_ACTIVITY_CLEAR_TOP va a buscar la nueva actividad en la pila, estará allí y se detendrá antes de que se elimine todo lo demás.

Aquí está mi función de cierre de sesión; El parámetro Ver es el botón al que se adjunta la función.

public void onLogoutClick(final View view) { Intent i = new Intent(this, Splash.class); i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); startActivity(i); finish(); }


Puedo sugerirte otro enfoque IMHO más robusto. Básicamente, debe transmitir un mensaje de cierre de sesión a todas sus actividades que necesiten permanecer bajo un estado de inicio de sesión. Por lo tanto, puede utilizar sendBroadcast e instalar un BroadcastReceiver en todas sus actividades. Algo como esto:

/** on your logout method:**/ Intent broadcastIntent = new Intent(); broadcastIntent.setAction("com.package.ACTION_LOGOUT"); sendBroadcast(broadcastIntent);

El receptor (Actividad asegurada):

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /**snip **/ IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction("com.package.ACTION_LOGOUT"); registerReceiver(new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { Log.d("onReceive","Logout in progress"); //At this point you should start the login activity and finish this one finish(); } }, intentFilter); //** snip **// }


Si está utilizando API 11 o superior, puede intentar esto: developer.android.com/reference/android/content/… - parece estar abordando exactamente el problema que está teniendo. Obviamente, la multitud pre-API 11 tendría que usar alguna combinación de hacer que todas las actividades verifiquen un extra, como sugiere @doreamon, o algún otro truco.

(También tenga en cuenta: para usar esto, debe pasar FLAG_ACTIVITY_NEW_TASK )

Intent intent = new Intent(this, LoginActivity.class); intent.putExtra("finish", true); // if you are checking for this in your other Activities intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(intent); finish();


Sugeriría un enfoque diferente a esta pregunta. Tal vez no sea la más eficiente, pero creo que es la más fácil de aplicar y requiere muy poco código. Escribir el siguiente código en su primera actividad (actividad de inicio de sesión, en mi caso) no permitirá al usuario volver a las actividades iniciadas anteriormente después de cerrar la sesión.

@Override public void onBackPressed() { // disable going back to the MainActivity moveTaskToBack(true); }

Supongo que LoginActivity se termina justo después de que el usuario inicie sesión, de modo que no pueda volver más tarde presionando el botón Atrás. En su lugar, el usuario debe presionar un botón de cerrar sesión dentro de la aplicación para cerrar la sesión correctamente. Lo que este botón de cierre de sesión implementaría es una simple intención de la siguiente manera:

Intent intent = new Intent(this, LoginActivity.class); startActivity(intent); finish();

Todas las sugerencias son bienvenidas.


Use esto, debería ser útil para usted. Respuesta xbakesx ligeramente modificada.

Intent intent = new Intent(this, LoginActivity.class); if(Build.VERSION.SDK_INT >= 11) { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK); } else { intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); } startActivity(intent);


ACTUALIZAR

El finishAffinity() super finishAffinity() ayudará a reducir el código pero logrará lo mismo. Terminará la actividad actual así como todas las actividades en la pila, usa getActivity().finishAffinity() si estás en un fragmento.

finishAffinity(); startActivity(new Intent(mActivity, LoginActivity.class));

RESPUESTA ORIGINAL

Suponga que LoginActivity -> HomeActivity -> ... -> SettingsActivity call signOut ():

void signOut() { Intent intent = new Intent(this, HomeActivity.class); intent.putExtra("finish", true); intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); // To clean up all activities startActivity(intent); finish(); }

InicioActividad:

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); boolean finish = getIntent().getBooleanExtra("finish", false); if (finish) { startActivity(new Intent(mContext, LoginActivity.class)); finish(); return; } initializeView(); }

Esto funciona para mí, espero que sea útil para ti también. :)