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 dejar que sólo fragmentos específicos desplazarse dentro de un ViewPager de CoordinatorLayout?
  • Análisis de Google con fragmento
  • Cómo mostrar un DialogFragment de un controlador
  • Android Studio no puede resolver el símbolo 'TabLayout'
  • Android No se pueden retener fragmentos que están anidados en otros fragmentos
  • ¿Cómo puedo crear pestañas para un ViewPager ahora que las pestañas ActionBar están obsoletas (Lollipop)
  • Android SupportLib - FrameLayout en CoordinatorLayout con AppBarLayout consumiendo toda la altura de la pantalla
  • ¿Cuál es el caso de uso de un Fragmento sin UI?
  • El fragmento dentro de VideoView hace que la pantalla negra se deslice
  • Java.lang.IllegalStateException: RecyclerView no tiene LayoutManager en Fragmento
  • Valor de restablecimiento de un entero a una hora específica del día
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.