java.lang.IllegalStateException: el fragmento ya no existe para la clave f1: índice 3

6 minutos de lectura

avatar de usuario
Usuario

Quiero entender esta excepción para implementar una solución adecuada.

Hay un ViewPager y usa un FragmentStatePagerAdapter para instanciar 2 fragmentos a través de getItem y MyFragmentClass.newInstance(...).

El getItem del adaptador se ve así:

@Override
public Fragment getItem(int position) {
    Fragment fragment = null;

    switch(position) {
        case 0:
            fragment = MyFragment2.newInstance(par1);
            break;
        case 1:
            fragment = MyFragment2.newInstance(par2, par3);
            break;
    }
    return fragment;
}

Problema:

Cuando la actividad se destruye y se vuelve a crear, el adaptador se instancia nuevamente, los fragmentos se crean nuevamente con MyFragmentClass.newInstance(...)… pero luego en esta línea:

pager.setAdapter(adapter);

Obtengo la excepción mencionada.

Busqué en la fuente donde se lanza la excepción, es esto:

@Override
public Fragment getFragment(Bundle bundle, String key) {
    int index = bundle.getInt(key, -1);
    if (index == -1) {
        return null;
    }
    if (index >= mActive.size()) {
        throw new IllegalStateException("Fragement no longer exists for key "
                + key + ": index " + index);
    }
    Fragment f = mActive.get(index);
    if (f == null) {
        throw new IllegalStateException("Fragement no longer exists for key "
                + key + ": index " + index);
    }
    return f;
}

Entonces se pasa un paquete allí, con algún estado que hace referencia a mis fragmentos antiguos, pero esto no corresponde al estado actual (mActive), y se lanza la excepción.

No entiendo cuál es la idea detrás de esto, o de qué manera se supone que debo instanciar los fragmentos.

Intenté un truco que obtuve de otro contexto:

pager.setOffscreenPageLimit(1);

Para evitar que los fragmentos se destruyan cuando están fuera de pantalla (en el caso del visor de 2 páginas, aunque no sé si funciona bien con el adaptador de estado). Pero no parece estar relacionado, al menos, no ayuda, sigue teniendo la misma excepción.

Al detectar la excepción, las páginas quedan en blanco.

  • ¿Encontraste una solución funcional para tu problema?

    – guardarpoblación

    26 de noviembre de 2014 a las 18:27

  • Encontraste alguna solución

    – Stefan Rasmusson

    17 de julio de 2015 a las 10:46

  • no recuerdo, lo siento 🙁

    – Usuario

    17 de julio de 2015 a las 10:58

  • La pregunta fue hecha hace 2 años. Hoy en día, se utilizan AppcompatActivity y support fragment. Utilice setRetianInstance(verdadero); y recuerde recrear el adaptador con elementos existentes en la orientación. Si no vuelve a crear el adaptador, intentará usar el contexto desechado, lo que provocará el bloqueo.

    – HBB20

    26/10/2015 a las 10:37

  • El mismo tema se trata en el hilo. code.google.com/p/android/issues/detail?id=54520

    – Conocer

    31 de marzo de 2016 a las 12:28

avatar de usuario
mikelis kaneps

Esto podría ayudar –

@Override
public Parcelable saveState() {
    return null;
}

Agregue la línea de arriba en FragmentStatePagerAdapter.

  • Esto funciona perfectamente para tratar con un adaptador de megafonía en la pila trasera. ¡Gracias!

    –Andy H.

    10 de agosto de 2015 a las 20:22

  • si estas evitando saveState() ya no necesitas usar FragmentStatePagerAdapter. Entonces solo tienes que usar FragmentPagerAdapter para trabajar

    – Lennon Spirlandelli

    29/01/2016 a las 19:01

  • @Nishad, ¿qué no es cierto?

    – Lennon Spirlandelli

    17 de febrero de 2016 a las 18:56

  • evitando saveState() por FragmentStatePagerAdapter no es equivalente a usar FragmentPagerAdapter. FragmentStatePagerAdapter destruye los objetos fragmentados cuando no están en uso FragmentPagerAdapter no es. Al evitar saveState() estamos pidiendo FragmentStatePagerAdapter para destruir fragmentos sin salvar el estado.

    – Nishad

    18 de febrero de 2016 a las 7:07

  • @Lennon FragmentPagerAdapter no usa saveState() porque todo el fragmento se retiene en la memoria. Si quieres evitar ese comportamiento que utilizas FragmentStatePagerAdapter que solo guarda el estado. Si no desea guardar nada en absoluto, utilice FragmentStatepagerAdapter sin estado de ahorro; automáticamente no guarda el Fragment en memoria.

    – Andrew Orobator

    18 de enero de 2017 a las 19:01

Si no desea que los fragmentos se recuperen cuando están fuera de la pantalla, debe usar FragmentPagerAdapter y no FragmentStatePagerAdapter.

  • ¿Cuál es la diferencia en esta situación?

    – TootsieRockNRoll

    28 de enero de 2014 a las 9:26

  • FragmentPagerAdapter carga cada fragmento en la memoria a medida que se visita. FragmentStatePagerAdapter libera su referencia a fragmentos una vez que han pasado el offscreenPageLimit parámetro.

    – un poco

    28 de enero de 2014 a las 10:22

  • Entonces, si establecemos un límite de página fuera de la pantalla, ¿ya no sería un problema? (Para el FragmentStatePagerAdapter)

    – TootsieRockNRoll

    28 de enero de 2014 a las 10:25


  • No, el cartel original estaba haciendo algo más mal, como configurar incorrectamente la cantidad de elementos.

    – un poco

    28 de enero de 2014 a las 10:38

avatar de usuario
vicky

Detalle del problema

De forma predeterminada, FragmentStatePagerAdapter guardará y restaurará el estado de ViewPager. Mientras se restaura si la instancia del fragmento se elimina debido a algún motivo, FragmentManger generará esta excepción.

Solución:

Para solucionar esto, es necesario anular el método restoreState en nuestro FragmentStatePagerAdapter y colocar el bloque try catch. Evitará el bloqueo y también conservará el estado de fragmento del visor para el caso normal.

@Override
public void restoreState(Parcelable state, ClassLoader loader) {
    try {
        super.restoreState(state, loader);
    } catch (Exception e) {
        Log.e("TAG", "Error Restore State of Fragment : " + e.getMessage(), e);
    }
}

Nota: Podemos usar FragmentPagerAdapter o Override saveState() y devolver nulo también soluciona este problema, pero el visualizador no conservará su estado en el caso normal.

  • Gracias @Vicky, funciona de maravilla, esta debe marcarse como la respuesta correcta.

    – Muhammad Faizán

    30 de noviembre de 2019 a las 6:39

  • no se puede anular, se establece como final 🙁

    – Daniyal Javaid

    20 abr 2021 a las 23:02

si está utilizando ViewPager2, utilice este método en el objeto ViewPager2

viewPager2.setSaveEnabled(false);

avatar de usuario
yi wang

Tuve problemas con este problema durante todo el día, pero ahora encontré la solución.

private ViewPager _mViewPager;
_mViewPager.setOffscreenPageLimit(5);
//5 is how much page you have.

setOffscreenPageLimit Establezca el número de páginas que deben conservarse a ambos lados de la página actual en la jerarquía de vistas en estado inactivo. Las páginas que superen este límite se recrearán desde el adaptador cuando sea necesario.

  • esto usa una tonelada de memoria… ahorra la molestia de no guardar el estado y todo eso… pero esto es malo a largo plazo cuando hay una tonelada de datos

    – Kushán

    1 de febrero de 2017 a las 21:22

avatar de usuario
Ilya Gazman

Use Ciclo de vida de actividad en lugar de Fragmento.

public class MyAdapter extends FragmentStateAdapter {

    public MyAdapter (@NonNull Fragment fragment) {
        super(fragment.getFragmentManager(), fragment.getActivity().getLifecycle());
    }
}

O como otros mencionaron deshabilitar el ViewPager2 guardar Estado.

  • en Diseño: android:saveEnabled="false"
  • en codigo:
    • viewPager.setSaveEnabled(false);
    • viewPager.setSaveFromParentEnabled(false);

  • esto usa una tonelada de memoria… ahorra la molestia de no guardar el estado y todo eso… pero esto es malo a largo plazo cuando hay una tonelada de datos

    – Kushán

    1 de febrero de 2017 a las 21:22

avatar de usuario
任非凡

Muy bueno ! Ya que se repite pager.setAdapter(adapter); cuando llame restoreState Causa excepción:

Fragment no longer exists for key f0: index 0

podemos liberar fragmento RootView

    public void onDestroyView() {
    super.onDestroyView();
    if (isRecyclerRootViewAlways()) {
        mRootView = null;//<--
    }
    mMyFragmentLifecycle.onFragmentDestroyView(this);
}

¿Ha sido útil esta solución?