TextView.setText() con alta frecuencia : fuga de memoria

Este articulo presente porqué Android TextView esta responsable de una fuga de memoria cuando necesita de actualizar texto con alta frecuencia, y como hacer para evitar lo.Atención: comparto mi experiencia de debutante en Java sobre Android quien hubiera querido leer un articulo así antes. Es posible que paso al lado de algo.

problema: EL importante creación de objetos

Exhibo todas las 50ms las nuevas valores de los sensores: acelerómetro, giroscopio, y otros sensores qué pueden ser resultados directos del hardware o de la fusión de otros sensores. Ahí está lo que pasa al nivel de la memoria :

Ejemplo de la memoria RAM qué dispararse
Cada 15 segundas, el garbage collector vacía la memoria

Sin tener cuenta de los pequeñitos mili-segundos de pausas impuestas por la liberación de la memoria, el garbage collector no se pretende a ser tan solicitando. Después qué limpié todo mi código de las construcciones de objetos (especialmente el objeto String) de la función llamada, me he interesado a TextView, e hice un análisis de la memoria:

Resultado de la análisis de memoria

Cada llamada a setText() conlleva a la creación de un nuevo StaticLayout, y evalúa exactamente las dimensiones del layout dependiente al su contenido. Excepto que cada creación  de StaticLayout conlleva a la creación de muchos objetos en memoria.

En mi caso, setText() esta ademas demasiado usado considerando la decena de layouts qué tengo que actualizar constantemente. Desgraciadamente, reducir todo el contenido a un layout solo no cambiara nada, en alta frecuencia la sola creación de un objeto  StaticLayout consume ya bastante de memoria. Hay que abstenerse al máximo la creación de objetos.

UNA SOLUCIÓN : Dejar TextView

Para evitar eso, luego creé un nuevo tipo de View que actúa como un layout especializado, de dimensiones conocidas (porqué el contenido toma siempre el mismo tamaño de altura y casi el mismo tamaño de longitud) al cual modifico su evento onDraw() qué es el evento llamado para después escribir texto manualmente con Canvas.drawText(), con el objetivo de pasar a través del comportamiento básico de un TextView.

public class SensorView extends View {

    ...

    private final char[] buffer = new char[BUFFER_SIZE];
    private final Paint paint = new Paint();

    public SensorView(Context context) {
        super(context);
        this.initPaint();
    }

    public SensorView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // Get back some values from xml data
        this.padding = attrs.getAttributeIntValue("http://schemas.android.com/apk/res/android", "padding", this.padding);
        this.textSize = attrs.getAttributeIntValue("http://schemas.android.com/apk/res/android", "padding", this.textSize);
        this.initPaint();
    }

    public void initPaint() {

        // Setting the paint
        this.padding = getPixels(TypedValue.COMPLEX_UNIT_DIP, this.padding);
        this.textSize = getPixels(TypedValue.COMPLEX_UNIT_SP, this.textSize);

        this.paint.setAntiAlias(true);
        this.paint.setColor(Color.DKGRAY);
        this.paint.setTextSize(this.textSize);
    }

    @Override
    public void onDraw(Canvas canvas) {

        int start = 0, line = 0;

        // Display multi-lines content
        for (int i = 0; i < BUFFER_SIZE; i++)
        {
            if (buffer[i] == '\n' || buffer[i] == '\0') {
                canvas.drawText(buffer, start, i-start, this.padding, line++ * this.textSize * LINE_HEIGHT + this.padding, paint);
                start = i+1;
            }
        }
    }

    public void setText(StringBuilder source) {
        source.getChars(0, source.length(), buffer, 0);
    }

    private int getPixels(int unit, float size) {
        DisplayMetrics metrics = Resources.getSystem().getDisplayMetrics();
        return (int)TypedValue.applyDimension(unit, size, metrics);
    }
}

Para cada modificación, llamo SensorView.postInvalidate() qué invalida la View y con consecuencia llama una otra vez onDraw(). Por supuesto, no aprovecho de las optimizaciones de rendimiento relacionadas con TextView, pero de todas formas mi texto siempre no sea el mismo et por eso no cumpliera cualquier sistema de caché. Ahí esta el nuevo resultado al nivel de la memoria:

Ejemplo del estado de la memoria después de la corrección
Después de la corrección, no hay más fuga de memoria.

¡ Problema solventado !

Dejá un comentario

Tu dirección de correo electrónico no será publicada. Los campos necesarios están marcados *

This site uses Akismet to reduce spam. Learn how your comment data is processed.