Pregunta Cómo destruir fragmentos viejos en FragmentStatePagerAdapter


Quiero implementar esto: enter image description here
Uso ViewPager con FragmentStatePagerAdapter.
Empecé con el ejemplo de esta página:
http://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html

Este es mi adaptador ViewPager:

    public static class MyAdapter extends FragmentStatePagerAdapter {
        public MyAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        @Override
        public Fragment getItem(int position) {
            return ArrayListFragment.newInstance(position);
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            super.destroyItem(container, position, object);
        }
    }

Cada página de mi ViewPager contiene un ListView con algunos datos. En el momento en que cambie a una nueva página en ViewPager, aumentará la memoria RAM muy rápidamente.
¿Cómo debo eliminar los viejos fragmentos?
También usé esto pero no hace nada:

public void destroyItem(ViewGroup container, int position, Object object) {

    FragmentManager manager = ((Fragment) object).getFragmentManager();
    FragmentTransaction trans = manager.beginTransaction();
    trans.remove((Fragment) object);
    trans.commit();

    super.destroyItem(container, position, object);
}

También hay un retraso de 1-2 segundos después de cambiar rápidamente a una nueva página o página anterior. ¿Hay alguna técnica para eliminar esa demora? Si cambio a una nueva página y espero durante 2 segundos, en el próximo cambio no habrá más demora.

Probado en Nexus 7.


32
2018-06-06 23:43


origen


Respuestas:


No deberías tratar de interferir con la forma en que Android administra tu Fragment implementaciones. El valor predeterminado para el setOffScreenPageLimit ya debería ser uno. Esto significa que Android destruirá fragmentos viejos cuando la memoria se agote. Mientras no tengas un problema de memoria, solo déjalo.

La razón por la cual tu memoria aumenta es porque Android mantiene Fragment instancias en la memoria para poder volver a conectar con ellos en lugar de tener que crear instancias. Te recomiendo que tengas en cuenta la contingencia de tu Fragment las instancias son destruidas por el sistema operativo, guardando su estado si eso sucede, y deja que el SO haga su trabajo.

La demora que está experimentando podría deberse a un cálculo intenso en el hilo de la interfaz de usuario. Si es así, sugiero moverlo a, por ejemplo, un AsyncTask. Sin el código, sin embargo, solo se trata de adivinar qué podría causar el problema. Pero al haber solo una demora inicial, sugiere que está cargando algo que podría bloquear el hilo de UI.

Actualizar: Mira esto https://stackoverflow.com/a/9646622/170781 que describe muy claramente cómo el ViewPager maneja Fragment instancias.


14
2017-08-21 08:59



Yo tuve el mismo problema. Pero en mi caso ViewPager estaba dentro del otro fragmento. y después de eliminar ViewPagerFragment de FragmentManager, todos los fragmentos de FragmentStatePagerAdapter se mantienen en el administrador de fragmentos. entonces, después de algunos de esos cambios, fue OutOfMemoryError. Luego enciendo los registros de FragmentManager por:

FragmentManager.enableDebugLogging(true);

Y descubrió que la identificación de cada fragmento nuevo aumenta cada vez. Sucede solo con StatePagerAdapter. Para resolver este problema, llamo eliminar para cada fragmento que se haya instanciado.

protected void dispatchOnDetach(Iterable<Fragment> fragments) {
    if (fragments == null)
        return;

    Activity aa = getActivity();
    if (aa == null)
        return;

    IBaseActivity ba = (IBaseActivity) aa;
    if (ba.isActivityStopped())
        return;

    FragmentManager frMan = ba.getSupportFragmentManager();
    FragmentTransaction frTr = frMan.beginTransaction();

    for (Fragment fr : fragments) {
        if (fr != null) {
            frTr.remove(fr);
        }
    }

    frTr.remove(this);
    frTr.commit();

}

En tu caso. si no cambia ViewPager durante el tiempo de ejecución, es posible que ese recolector de basura no pueda destruir sus fragmentos incluso después de eliminarlos del administrador de fragmentos debido a algunas referencias a ellos. Debería verificar si algunas clases globales los usan.

Y para fines de optimización, puede almacenar en caché cada fragmento instanciado mediante SoftReference o LruCache. Ejemplo:

public class MyAdapter extends FragmentStatePagerAdapter {

private final LruCache<Integer, Fragment> mCache;

public MyAdapter(FragmentManager fm) {
    super(fm);
    mCache = new LruCache<Integer, Fragment>(10);
}

@Override
public int getCount() {
    return NUM_ITEMS;
}

@Override
public Fragment getItem(int position) {
    return mCache.get(position);
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
}

private class MyCache extends LruCache<Integer, Fragment> {

    public MyCache(int maxSize) {
        super(maxSize);
    }

    @Override
    protected Fragment create(Integer key) {
        return ArrayListFragment.newInstance(key);
    }
}
}

14
2017-08-21 14:55



los FragmentStatePagerAdapter ya es muy frugal con la memoria, ya que destruye su innecesario fragmentsautomáticamente. Simplemente mantiene las vistas de los fragmentos directamente a la izquierda y derecha del elemento que se muestra actualmente y destruye las demás.

Ejemplo: Entonces, una vez que deslizas hacia la dirección correcta, precarga el fragmento que pronto estará a la derecha y destruye el fragmento que ahora tiene dos ranuras a la izquierda del fragmento visualizado actualmente.


3
2017-08-15 11:50



Creo que el problema no es con ViewPager está en ListFragments. ¿Qué tipo de contenido muestra en ellos? ¿Asignas muchas imágenes? ¿Puedes publicar el código de tu ListFragment?

Preferiría hacer un comentario, pero como no tengo suficientes puntos espero ayudar editando esta respuesta.


1
2017-08-20 13:19



ViewPager en sí tiene un método setOffscreenPageLimit que le permite especificar el número de páginas guardadas por el adaptador. Entonces tus fragmentos que están muy lejos serán destruidos.

Es un poco difícil decir cuál puede ser su problema particular porque no sé lo que hace su fragmento. Por el sonido de la demora de 1-2 segundos parece que podrías estar trabajando en el hilo de la interfaz de usuario. Además, ¿qué más estás haciendo en tu fragmento que consume memoria? ¿Tal vez estás cargando imágenes en algún caché de memoria estática y no las liberas al eliminar fragmentos? ¿Podrías proporcionar tu código de fragmento para ver qué está haciendo?

En general, recomendaría que se descargue un archivo HPROF de su aplicación en el momento en que se requiera memoria extra y se analicen las referencias mediante MAT (herramienta de análisis de memoria). Está claro que tiene problemas de pérdida de memoria y dudo mucho que el problema esté en que los Fragmentos no se destruyan.

En caso de que no sepas cómo analizar el montón de memoria, aquí hay una buena vídeo. No puedo contar cuántas veces me ayudó a identificar y eliminar las pérdidas de memoria en mis aplicaciones.


1
2017-08-21 08:42



Anule esto en FragmentStatePagerAdapter, observe el ligero cambio.

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    if (position >= getCount()) {
    FragmentManager manager = ((Fragment) object).getFragmentManager();
    FragmentTransaction trans = manager.beginTransaction();
    trans.remove((Fragment) object);
    trans.commit();
}

1
2018-02-02 09:16