vida tipos savedinstancestate onstop intent ciclo activity android android-intent android-fragments android-activity

tipos - Android parentActivity no se vuelve a crear después de que startActivityForResult regresa



startactivityforresult android (2)

Tengo una actividad principal y dentro de ella, estoy cargando un fragmento A. Desde FragmentA, estoy llamando a la actividad de google placepicker usando startActivityforResult de la siguiente manera.

PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder(); Intent intent = builder.build(getActivity()); getActivity().startActivityForResult(intent,PLACE_PICKER_REQUEST);

Pero cuando selecciono el lugar, onActivityResult (ya sea en FragmentA o MainActivity) no se está llamando. De hecho, mi aplicación se destruye después de la llamada a startActivityForResult.

Según mi entendimiento, Android debería recrear la actividad de llamadas si no está disponible en la memoria. Pero no está sucediendo. Incluso en Create no se llama dentro de MainActivity.

¿Alguien podría decirme la razón detrás de este tipo de comportamiento o me falta algo?

Ahora, en lugar de PlacePicker Activity, he intentado utilizar otra actividad en la misma aplicación.

Digamos que tengo MainActivity con FragmentA cargado. Estoy llamando SubActivity con startActivityForResult desde FragmentA . Ahora, al regresar de SubActivity , la aplicación sale. He habilitado Dont keep activities en mi dispositivo para probar este escenario específico. Puedo ver que MainActivity se destruye cuando me paso a SubActivity Pero al regresar de SubActivity , Android no está recreando MainActivity (incluso onCreate no se está llamando. La aplicación simplemente sale).


Esto puede suceder debido a muchas razones, pero con suerte es algo raro. El sistema operativo destruirá la actividad cuando esté en segundo plano si necesita reclamar recursos, lo cual es más probable que ocurra en dispositivos con menos memoria y potencia de procesamiento.

Usar la configuración Do not keep Activities es una buena manera de probar este escenario, y en este caso hay otros problemas incluso si la Actividad / Fragmento se vuelve a crear. Con esta configuración habilitada, la Actividad y el Fragmento se destruyen cuando se muestra el PlacePicker, y cuando onActivityResult() no hay un Contexto válido porque la Actividad y el Fragmento aún están en proceso de ser recreados.

Descubrí esto realizando una prueba controlada con la configuración deshabilitada, y luego con la configuración habilitada, y luego mirando los resultados

Puse el registro en cada devolución de llamada del ciclo de vida para la Actividad y el Fragmento para tener una idea de lo que está sucediendo durante estas llamadas.

Aquí está la clase completa que utilicé, que incluye tanto la Actividad como el Fragmento:

public class MainActivity extends AppCompatActivity { MyFragment myFrag; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.d("PlacePickerTest", "Activity onCreate"); myFrag = new MyFragment(); setContentView(R.layout.activity_main); if (savedInstanceState == null) { getSupportFragmentManager().beginTransaction() .add(R.id.container, myFrag) .commit(); } } @Override protected void onResume() { super.onResume(); Log.d("PlacePickerTest", "Activity onResume"); } @Override protected void onPause() { super.onPause(); Log.d("PlacePickerTest", "Activity onPause"); } @Override protected void onDestroy() { Log.d("PlacePickerTest", "Activity onDestroy"); super.onDestroy(); } @Override public boolean onCreateOptionsMenu(Menu menu) { getMenuInflater().inflate(R.menu.menu_main, menu); return true; } @Override public void onActivityResult (int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); Log.d("PlacePickerTest", "Activity onActivityResult requestCode:" + requestCode); if (requestCode == 199){ //process result of PlacePicker in the Fragment myFrag.processActivityResult(data); } } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); if (id == R.id.action_settings) { //open PlacePicker from menu item myFrag.startPlacePicker(); return true; } return super.onOptionsItemSelected(item); } /** * Fragment containing a map and PlacePicker functionality */ public static class MyFragment extends Fragment { private GoogleMap mMap; Marker marker; public MyFragment() { } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View rootView = inflater.inflate(R.layout.fragment_main, container, false); Log.d("PlacePickerTest", "Fragment onCreateView"); return rootView; } @Override public void onResume() { super.onResume(); Log.d("PlacePickerTest", "Fragment onResume"); setUpMapIfNeeded(); } @Override public void onPause() { super.onPause(); Log.d("PlacePickerTest", "Fragment onPause"); } @Override public void onDestroy() { Log.d("PlacePickerTest", "Fragment onDestroy"); super.onDestroy(); } private void setUpMapIfNeeded() { // Do a null check to confirm that we have not already instantiated the map. if (mMap == null) { // Try to obtain the map from the SupportMapFragment. mMap = ((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)) .getMap(); // Check if we were successful in obtaining the map. if (mMap != null) { setUpMap(); } } } private void setUpMap() { // Enable MyLocation Layer of Google Map mMap.setMyLocationEnabled(true); mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); mMap.getUiSettings().setZoomControlsEnabled(true); mMap.getUiSettings().setMyLocationButtonEnabled(true); mMap.getUiSettings().setCompassEnabled(true); mMap.getUiSettings().setRotateGesturesEnabled(true); mMap.getUiSettings().setZoomGesturesEnabled(true); } public void startPlacePicker(){ int PLACE_PICKER_REQUEST = 199; PlacePicker.IntentBuilder builder = new PlacePicker.IntentBuilder(); //Context context = getActivity(); try { Log.d("PlacePickerTest", "Fragment startActivityForResult"); getActivity().startActivityForResult(builder.build(getActivity()), PLACE_PICKER_REQUEST); } catch (GooglePlayServicesRepairableException e) { e.printStackTrace(); } catch (GooglePlayServicesNotAvailableException e) { e.printStackTrace(); } } public void processActivityResult ( Intent data) { if (getActivity() == null) return; Log.d("PlacePickerTest", "Fragment processActivityResult"); //process Intent...... Place place = PlacePicker.getPlace(data, getActivity()); String placeName = String.format("Place: %s", place.getName()); String placeAddress = String.format("Address: %s", place.getAddress()); LatLng toLatLng = place.getLatLng(); // Show the place location in Google Map mMap.moveCamera(CameraUpdateFactory.newLatLng(toLatLng)); mMap.animateCamera(CameraUpdateFactory.zoomTo(15)); if (marker != null) { marker.remove(); } marker = mMap.addMarker(new MarkerOptions().position(toLatLng) .title(placeName).snippet(placeAddress) .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA))); } } }

Aquí están los registros resultantes que dan una idea de las llamadas de ciclo de vida que se llaman durante el proceso en circunstancias normales:

D/PlacePickerTest﹕ Activity onCreate D/PlacePickerTest﹕ Fragment onCreateView D/PlacePickerTest﹕ Activity onResume D/PlacePickerTest﹕ Fragment onResume D/PlacePickerTest﹕ Fragment startActivityForResult D/PlacePickerTest﹕ Fragment onPause D/PlacePickerTest﹕ Activity onPause D/PlacePickerTest﹕ Activity onActivityResult requestCode:199 D/PlacePickerTest﹕ Fragment processActivityResult D/PlacePickerTest﹕ Activity onResume D/PlacePickerTest﹕ Fragment onResume

Entonces, como puede ver en onDestroy() nunca se llama, y onPause() y onResume() se onResume() tanto en la Actividad como en el Fragmento.

Aquí está el resultado visualmente:

Luego, después de elegir un lugar:

Luego, habilité Do not keep Activities en Developer Options en Settings, y ejecuté la misma prueba.

Estos son los registros resultantes:

D/PlacePickerTest﹕ Activity onCreate D/PlacePickerTest﹕ Fragment onCreateView D/PlacePickerTest﹕ Activity onResume D/PlacePickerTest﹕ Fragment onResume D/PlacePickerTest﹕ Fragment startActivityForResult D/PlacePickerTest﹕ Fragment onPause D/PlacePickerTest﹕ Activity onPause D/PlacePickerTest﹕ Activity onDestroy D/PlacePickerTest﹕ Fragment onDestroy D/PlacePickerTest﹕ Activity onCreate D/PlacePickerTest﹕ Fragment onCreateView D/PlacePickerTest﹕ Activity onActivityResult requestCode:199 D/PlacePickerTest﹕ Activity onResume D/PlacePickerTest﹕ Fragment onResume

Por lo tanto, puede ver que tanto la Actividad como el Fragmento se destruyen cuando se muestra el PlacePicker, y una vez que se selecciona el PlacePicker, la ejecución del código nunca llegó a la entrada del registro Fragment processActivityResult , y la aplicación nunca mostró el lugar escogido en el mapa.

Esto se debe a la verificación de contexto nula:

if (getActivity() == null) return; Log.d("PlacePickerTest", "Fragment processActivityResult"); //process Intent...... Place place = PlacePicker.getPlace(data, getActivity());

Entonces, la llamada a onActivityResult() sí aparece, pero al mismo tiempo que la actividad y el fragmento se vuelven a crear, y necesita un contexto válido para hacer la llamada a PlacePicker.getPlace(data, getActivity()); .

La buena noticia es que la mayoría de los usuarios finales no tendrán habilitada la configuración Do not keep Activities , y la mayoría de las veces su actividad no será destruida por el sistema operativo.


Parece bastante inusual para Android limpiar una actividad de la manera que describió, pero si ese fuera el caso, entonces su actividad aún debería restaurarse. Android no debe destruir la actividad, a menos que llame específicamente a finish() o algo cause que la actividad termine prematuramente.

Si se refiere al diagrama del ciclo de vida de la actividad:

En el escenario que describió, la primera actividad debería invocar aStop, pero no aDestroy, luego, cuando regrese de la segunda actividad, debería invocarInicio de nuevo.

Creé una aplicación muy simple para probar el escenario que describiste, que contenía lo siguiente:

  • Hay 2 actividades, FirstActivity y SecondActivity
  • FirstActivity tiene un botón, cuando se hace clic en el botón, se inicia SecondActivity con startActivityForResult()
  • Los eventos del ciclo de vida de la actividad se registran usando ActivityLifecycleCallbacks en una clase de aplicación personalizada
  • En FirstActivity onActivityResult además, las salidas al registro cuando se llama

Aquí está lo que se produce:

La aplicación se inicia (FirstActivity se crea, inicia y es visible):

FirstActivity onCreate FirstActivity onStart FirstActivity onResume

Presiono el botón para iniciar SecondActivity:

FirstActivity onPause SecondActivity onCreate SecondActivity onStart SecondActivity onResume FirstActivity onSaveInstanceState FirstActivity onStop

Tenga en cuenta que onDestroy no se llama.

Ahora presiono el botón Atrás y regreso a la primera actividad:

SecondActivity onPause FirstActivity onStart FirstActivity onActivityResult FirstActivity onResume SecondActivity onStop SecondActivity onDestroy

El botón Atrás finish en SecondActivity, por lo que se destruye

Ahora si presiono nuevamente, FirstActivity también estará terminado, haciendo que se onDestroy a onDestroy .

FirstActivity onPause FirstActivity onStop FirstActivity onDestroy

Puede ver que este ejemplo se ha adherido exactamente al diagrama del ciclo de vida. Las actividades solo se destruyen una vez que se presiona el botón Atrás, lo que hace que la actividad llame a finish() .

Mencionó que intentó activar "No guardar actividades" en las opciones de desarrollador, podemos repetir el experimento anterior que esta opción habilitó y ver qué sucede. Acabo de agregar los eventos relevantes del ciclo de vida para guardar la repetición de todo lo que está arriba:

Después de presionar el botón en la primera actividad para comenzar la segunda actividad:

... SecondActivity onResume FirstActivity onSaveInstanceState FirstActivity onStop FirstActivity onDestroy

Como se esperaba, la actividad ha sido destruida esta vez. Esto es lo que sucede cuando vuelves a la primera actividad:

SecondActivity onPause FirstActivity onCreate FirstActivity onStart FirstActivity onActivityResult FirstActivity onResume ...

Esta vez se llamó de nuevo a onCreate porque el sistema no tenía una versión detenida de la primera actividad para reiniciar. También onActivityResult() todavía se llamaba, independientemente del hecho de que la actividad tuviera que volver a crearse.

Esto también respalda que algo en su primera actividad debe estar llamando a finish() o haciendo que se cuelgue. Sin embargo, sin ver su código real, esto es conjetura.

Finalmente, para mantener el estado si su actividad por algún motivo necesita ser recreada, puede anular onSaveInstanceState() y agregar cualquier información de estado al paquete:

protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString(MY_STRING_KEY, "my string value"); }

Cuando vuelva a crear la actividad, volverá a obtener un paquete en Crear que debería contener todo lo que guardó:

protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... if (savedInstanceState != null) { // Restore previous state } }