¿Cómo actualizar las vistas de texto de Android de manera eficiente?
Estoy trabajando en una aplicación de Android que encuentra problemas de rendimiento. Mi objetivo es recibir cadenas de un AsyncTask y mostrarlos en un TextView. El TextView está inicialmente vacío y cada vez que el otro proceso envía una cadena concatena el contenido actual de la vista de texto. Actualmente utilizo StringBuilder para almacenar la cadena principal y cada vez que recibo una nueva cadena, la añado al StringBuilder y llamo
myTextView.setText(myStringBuilder.toString())
El problema es que el proceso de fondo puede enviar hasta 100 cadenas por segundo, y mi método no es lo suficientemente eficiente.
- Compresión de mapa de bits y optimización de velocidad en Android
- FindViewById vs referencia local en la actividad
- El desplazamiento y el zoom de MapView es lento después de agregar muchas superposiciones
- Android Studio generado APK se bloquea al ejecutar la aplicación en el mismo teléfono funciona bien. ¿Qué está mal?
- El estudio de Android toma demasiada memoria
Redibujar todo el TextView cada vez es, obviamente, una mala idea (complejidad de tiempo O (N²)), pero no estoy viendo otra solución …
¿Conoces una alternativa a TextView que podría hacer estas concatenaciones en O (N)?
- Dibujar sobre 4K polylines en android google maps
- Android java.lang.OutOfMemoryError?
- Android ble conectar lentamente
- ¿Cómo puedo evitar los retrasos de recolección de basura en juegos Java? (Mejores Prácticas)
- Acerca de la optimización de la implementación de patrones de ViewHolder en ListView
- Cómo limitar la velocidad de la red del dispositivo Android para propósitos de prueba
- ¿Cómo puedo dividir mi archivo en bloques de datos y trasfer los bloques de mi aplicación android?
- La notificación con setGroupSummary (true) no está visible en Android N
Siempre y cuando haya una nueva línea entre las cadenas, podría usar un ListView para anexar las cadenas y mantener las propias cadenas en un ArrayList o LinkedList a la que se agrega como AsyncTask recibe las cadenas.
También podría considerar simplemente invalidar el TextField con menos frecuencia; Digamos 10 veces por segundo. Esto sin duda mejorará la capacidad de respuesta. Algo como lo siguiente podría funcionar:
static long lastTimeUpdated = 0; if( receivedString.size() > 0 ) { myStringBuilder.append( receivedString ); } if( (System.currentTimeMillis() - lastTimeUpdated) > 100 ) { myTextView.setText( myStringBuilder.getChars( 0, myStringBuilder.length() ); }
Si las cadenas vienen en ráfagas, de tal manera que tiene un retraso entre ráfagas mayor que, digamos, un segundo, restablezca un temporizador cada actualización que disparará este código para que se ejecute de nuevo para captar la parte posterior de la última ráfaga.
Finalmente encontré una respuesta con la ayuda de havexz y Greyson aquí, y algo de código aquí . Como las cadenas estaban llegando en ráfagas, he decidido actualizar la interfaz de usuario cada 100ms. Para que conste, aquí está el aspecto de mi código:
private static boolean output_upToDate = true; /* Handles the refresh */ private Handler outputUpdater = new Handler(); /* Adjust this value for your purpose */ public static final long REFRESH_INTERVAL = 100; // in milliseconds /* This object is used as a lock to avoid data loss in the last refresh */ private static final Object lock = new Object(); private Runnable outputUpdaterTask = new Runnable() { public void run() { // takes the lock synchronized(lock){ if(!output_upToDate){ // updates the outview outView.setText(new_text); // notifies that the output is up-to-date output_upToDate = true; } } outputUpdater.postDelayed(this, REFRESH_INTERVAL); } };
Y lo puse en mi método onCreate()
:
outputUpdater.post(outputUpdaterTask);
Algunas explicaciones: cuando mi aplicación llama a su método onCreate()
, mi outputUpdater
recibe una solicitud para actualizar. Pero esta tarea ( outputUpdaterTask
) se pone una petición de actualización 100ms más tarde. El lock
se comparte con el proceso que envía las nuevas cadenas y establece output_upToDate en false.
Trate de limitar la actualización. Así que en lugar de actualizar 100 veces por segundo, ya que es la tasa de generación. Mantenga las 100 cadenas en el constructor de cadenas y luego actualice una vez por segundo.
El código debe gustar:
StringBuilder completeStr = new StringBuilder(); StringBuilder new100Str = new StringBuilder(); int counter = 0; if(counter < 100) { new100Str.append(newString); counter++; } else { counter = 0; completeStr.append(new100Str); new100Str = new StringBuilder(); myTextView.setText(completeStr.toString()); }
NOTA: El código de arriba es sólo para ilustración, así que puede que tenga que modificarlo según sus necesidades.
- Establecer el brillo de la pantalla de Android
- ¿Cómo cambiar programaticamente un entorno global como 'feedback haptic'?