FindFragmentByTag null para Fragmento A, si setRetain (true) en Fragmento B

Mi problema implica una actividad que alberga tres fragmentos de soporte. Uno es un fragmento programático normal (llamémoslo un fragmento de casa). Uno es un fragmento de retrato añadido en la parte superior del fragmento de origen cuando el dispositivo está orientado, y uno es "sin cabeza", para continuar una tarea asíncrona independientemente de los cambios de configuración. Muy simple, estaba trabajando en este bonito ejemplo .

public class HeadlessCustomerDetailFetchFragment extends Fragment{ private RequestCustomerDetails mRequest; private AsyncFetchCustomerDetails mAsyncFetchCustomerDetails; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); mRequest = (RequestCustomerDetails)getActivity(); } public void startFetching(String scannedBarcode) { if(mAsyncFetchCustomerDetails != null && mAsyncFetchCustomerDetails.getStatus() == AsyncTask.Status.RUNNING) return; if(mAsyncFetchCustomerDetails == null || mAsyncFetchCustomerDetails.getStatus() == AsyncTask.Status.FINISHED) mAsyncFetchCustomerDetails = new AsyncFetchCustomerDetails(getActivity(), mRequest, mPartner, scannedBarcode); } public void stopFetching() { if(mAsyncFetchCustomerDetails != null && mAsyncFetchCustomerDetails.getStatus() != AsyncTask.Status.RUNNING) return; mAsyncFetchCustomerDetails.cancel(true); } 

}

En la actividad onCreate () de mi actividad, creo y agrego el fragmento headless si es necesario.

  mHeadlessCustomerDetailFetchFragment = (HeadlessCustomerDetailFetchFragment)getSupportFragmentManager() .findFragmentByTag(HeadlessCustomerDetailFetchFragment.class.getSimpleName()); if(mHeadlessCustomerDetailFetchFragment == null) { mHeadlessCustomerDetailFetchFragment = HeadlessCustomerDetailFetchFragment.instantiate(this, HeadlessCustomerDetailFetchFragment.class.getName()); getSupportFragmentManager().beginTransaction() .add(mHeadlessCustomerDetailFetchFragment, mHeadlessCustomerDetailFetchFragment.getClass().getSimpleName()) .commit(); getSupportFragmentManager().executePendingTransactions(); id = null; } 

A continuación, iniciar una tarea asíncrona (a través de mi función startFetching ()) después de un retraso de 6 segundos (para la prueba) arrancó en el onCreateView () del fragmento retrato que se agrega cuando la orientación cambia a retrato. El cambio de orientación se detecta en la actividad onCreate ():

 if (savedInstanceState == null) { // Do some initial stuff for the home fragment } else { getSupportFragmentManager().popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { //Launch portrait fragment FragmentLauncher.launchPortraitFragment(this); } 

Cuando la tarea está terminada, vuelvo a la actividad e intento de actualizar la interfaz de usuario del fragmento de retrato activo, pero el gestor de fragmentos no puede encontrarlo, findFragmentByTag () devuelve null .

Para ser claro:

  • La etiqueta es correcta
  • El fragmento se encuentra si no oriento el dispositivo, y en su lugar iniciar la tarea asíncrona en otro lugar, durante la actividad onResume () por ejemplo.
  • Si no le digo al fragmento sin cabeza que se retenga, perdiendo el beneficio de no recrearlo, el fragmento de retrato también se encuentra correctamente.
  • Depuración Puedo ver todos los 3 fragmentos en el gestor si el sin cabeza no está configurado para conservarse . Si es así, sólo puedo ver el fragmento sin cabeza.

Tal vez la retención de un fragmento mata agresivamente otros fragmentos que no se conservan o algo en ese sentido?

One Solution collect form web for “FindFragmentByTag null para Fragmento A, si setRetain (true) en Fragmento B”

La raíz del problema es cómo mantener la referencia a la actividad dentro del fragmento sin cabeza.
No está claro en el código proporcionado cómo actualizar UI después de completar AsyncTask, permite asumir que utiliza mRequest del primer fragmento de código. Usted da mRequest al constructor cuando necesita nuevo AsyncTask y usa esta referencia después de que AsyncTask se complete.
Está bien, cuando no hay ninguna rotación de pantalla entre el momento en que se crea la actividad y cuando se actualiza la interfaz de usuario. Es porque usas la referencia a la actividad que todavía está activa.
No está bien si gira la pantalla. Usted tiene nueva actividad cada vez después de la rotación. Pero mRequest se asigna sólo una vez cuando se crea un fragmento headless en la primera llamada de la actividad onCreate() . Por lo tanto, contiene referencia a la primera instancia de actividad que no está activa después de la rotación. Hay dos instancias de actividad después de la rotación en su caso: la primera – que se hace referencia por mRequest y la segunda – que es visible y activa. Puede confirmar esto registrando la referencia de actividad dentro de onCreate : Log.i(TAG, "onCreate: this=" + this); Y dentro del método de la actividad que actualiza la interfaz de usuario después de la tarea asíncrona: Log.i(TAG, "updating UI: this=" + this);
Además la primera actividad está en estado Destruido. Todos los fragmentos se separan de esta actividad y los fragmentos no retenidos son destruidos. Es por eso que findFragmentByTag devuelve null.
Si el fragmento onCreate() no está configurado para conservarse, entonces onCreate() recrea en cada llamada. Así mRequest siempre hace referencia a la última actividad creada con todos los fragmentos. En este caso findFragmentByTag no devuelve null.

Para evitar este problema sugiero:

  1. Utilice referencia débil para almacenar referencia de Actividad. Algo como esto:
    private WeakReference<RequestCustomerDetails> mRequest;
  2. Cree un método en HeadlessCustomerDetailFetchFragment para actualizar esta referencia.
    public void updateResultProcessor(RequestCustomerDetails requestCustomerDetails) { mRequest = new WeakReference(requestCustomerDetails); // Update ui if there is stored result of AsyncTask (see p.4b) }
  3. Llame a este método de la actividad onCreate () cada vez.
  4. Cuando termina AsyncTask:
    A) si mRequest.get() no es null luego actualiza la interfaz de usuario.
    B) si mRequest.get() es null entonces almacena el resultado dentro del fragmento headless y mRequest.get() en p.2.

    Una referencia débil permitirá al GC procesar la actividad destruida y establecer un nulo dentro de una referencia débil. Nulo en la referencia débil indicará que no hay interfaz de usuario y no hay nada que actualizar. Almacenar el resultado de AsyncTask en el fragmento headless permitirá utilizar este resultado para actualizar la interfaz de usuario después de su recreación.

    Espero que esto ayude. Lo siento por mi inglés. Si algo no está claro trataré de explicar.

  • Cómo mantener el estado del fragmento en la aplicación
  • Android.support.v4.app.getFragmentManager () devuelve null?
  • Android InstantiationException con fragmento (es público)
  • Cómo abrir un fragmento diferente en recyclerview OnClick
  • Biblioteca de soporte: Las animaciones de FragmentTransaction no funcionan
  • Android ActionBar siempre null
  • Manera de la insensatez a manejar Fragmento en cambio de la orientación
  • Habilitar el cambio de orientación de la pantalla cuando ciertos fragmentos son visibles e inhabilitar cuando otros
  • ¿Cómo puedo cambiar el color de fondo de ProgressDialog sin afectar la frontera en Android?
  • Android: ¿qué sustituyó android.support.v4.app.NavUtils en APIs posteriores?
  • RxJava como autobús de eventos?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.