Incluso si tu aplicación es rápida y responsiva, ciertas decisiones de diseño pueden causar problemas a los usuarios, debido a interacciones no planificadas con otras aplicaciones o diálogos, pérdida involuntaria de datos, bloqueos no intencionados, etcétera. Para evitar estos problemas, es útil comprender el contexto en el que se ejecutan tus aplicaciones y las interacciones del sistema que pueden afectarla. En resumen, debes esforzarte por desarrollar una aplicación que interactúe sin interrupciones con el sistema y con otras aplicaciones.
Un problema común de fluidez es cuando el proceso en segundo plano de una aplicación (por ejemplo, un servicio o receptor de emisión) muestra un diálogo en respuesta a algún evento. Esto puede parecer un comportamiento inofensivo, en especial cuando compilas y pruebas tu aplicación de forma aislada en el emulador. Sin embargo, cuando tu aplicación se ejecuta en un dispositivo real, es posible que no tenga el enfoque del usuario cuando el proceso en segundo plano muestra el diálogo. Por lo tanto, la aplicación podría mostrar su diálogo detrás de la aplicación activa o podría enfocar desde la aplicación actual y mostrar el diálogo frente a lo que el usuario estaba haciendo (como marcar una llamada telefónica, por ejemplo). Ese comportamiento no funcionaría para tu aplicación o para el usuario.
Para evitar estos problemas, tu aplicación debe usar la instalación del sistema adecuada para notificar al usuario: las clases Notification
. Con las notificaciones, tu aplicación puede indicarle al usuario que se produjo un evento mostrando un ícono en la barra de estado en lugar de enfocar e interrumpir al usuario.
Otro ejemplo de un problema de fluidez es cuando una actividad pierde sin querer el estado o los datos del usuario porque no implementa correctamente onPause() y otros métodos del ciclo de vida. O, si tu aplicación expone datos destinados a otras aplicaciones, debes exponerlos a través de un ContentProvider en lugar de, por ejemplo, hacerlo a través de una base de datos o un archivo sin procesar legible para cualquier usuario.
Lo que esos ejemplos tienen en común es que implican una buena cooperación con el sistema y otras aplicaciones. El sistema Android está diseñado para tratar las aplicaciones como una especie de federación de componentes con acoplamiento bajo, en lugar de fragmentos de código de caja negra. Esto te permite, como desarrollador, ver todo el sistema como una federación aún más grande de estos componentes. Esto te beneficia, ya que te permite realizar una integración limpia y sin interrupciones en otras aplicaciones, por lo que debes diseñar tu propio código para devolver el favor.
En este documento, se analizan los problemas frecuentes de interrupción y cómo evitarlos.
No descartes datos
Siempre ten en cuenta que Android es una plataforma para dispositivos móviles. Puede parecer obvio decirlo, pero es importante recordar que otra actividad (como la app "Llamada telefónica entrante") puede aparecer en una ventana emergente sobre tu propia actividad en cualquier momento. Esto activará los métodos onSaveInstanceState() y onPause(), y es probable que tu aplicación se cierre.
Si el usuario estaba editando datos en tu aplicación cuando apareció la otra actividad, es probable que la aplicación pierda esos datos cuando se cierre. a menos que primero guardes el trabajo en progreso. El "estilo Android" es hacer precisamente eso: las aplicaciones para Android que aceptan o editan entradas deben anular el método onSaveInstanceState() y guardar su estado de alguna manera adecuada. Cuando el usuario vuelva a visitar la aplicación, debería poder recuperar sus datos.
Un ejemplo clásico de un buen uso de este comportamiento es una aplicación de correo electrónico. Si el usuario estaba redactando un correo electrónico cuando se inició otra actividad, la aplicación debe guardar el correo electrónico en proceso como borrador.
No expongas datos sin procesar
Si no caminarías por la calle en ropa interior, tampoco deberían hacerlo los datos. Si bien es posible exponer ciertos tipos de aplicaciones para que todo el mundo los lea, por lo general, esta no es la mejor idea. Exponer datos sin procesar requiere que otras aplicaciones comprendan el formato de tus datos. Si cambias ese formato, romperás cualquier otra aplicación que no se actualice de manera similar.
El "estilo Android" es crear un ContentProvider para exponer tus datos a otras aplicaciones a través de una API limpia, bien pensada y que se pueda mantener. Usar un ContentProvider es muy parecido a insertar una interfaz de lenguaje Java para dividir y crear componentes de dos fragmentos de código estrechamente acoplados. Eso significa que podrás modificar el formato interno de tus datos sin cambiar la interfaz que expone ContentProvider, y esto sin afectar otras aplicaciones.
No interrumpas al usuario
Si el usuario ejecuta una aplicación (como la aplicación de teléfono durante una llamada), es casi seguro que lo hizo a propósito. Es por eso que debes evitar generar actividades, excepto en respuesta directa a las entradas del usuario de la actividad actual.
Es decir, no llames a startActivity() desde BroadcastReceivers o servicios que se ejecutan en segundo plano. Si lo haces, se interrumpirá cualquier aplicación que se esté ejecutando en el momento y generará un usuario molesto. Quizás lo que es peor: que tu actividad se convierta en un "bandido de combinación de teclas" y reciba parte de la entrada que el usuario estaba proporcionando a la actividad anterior. Según lo que haga tu aplicación, esto podría ser una mala noticia.
En lugar de generar IU de Activity directamente desde el segundo plano, debes usar NotificationManager para configurar notificaciones. Aparecerán en la barra de estado, y el usuario podrá hacer clic en ellos cuando lo desee para ver lo que tu aplicación tiene que mostrarle.
(Ten en cuenta que todo esto no se aplica a los casos en los que tu propia actividad ya está en primer plano; en ese caso, el usuario espera ver tu próxima actividad en respuesta a la entrada).
¿Tienes mucho que hacer? Hazlo en un subproceso
Si tu aplicación necesita realizar un procesamiento costoso o de larga duración, probablemente debas moverla a un subproceso. Esto evitará que se muestre al usuario el temido diálogo "La aplicación no responde", y el resultado final será la desaparición feroz de tu aplicación.
De forma predeterminada, todo el código de una actividad, así como todas sus vistas, se ejecuta en el mismo subproceso. Este es el mismo subproceso que también procesa eventos de IU. Por ejemplo, cuando el usuario presiona una tecla, se agrega un evento de tecla presionada a la cola del subproceso principal de la actividad. El sistema de controlador de eventos debe quitar de la cola ese evento y controlarlo rápidamente. Si no es así, el sistema concluye unos segundos de que la aplicación se cuelga y ofrece cerrarla para el usuario.
Si tienes un código de larga duración, ejecutarlo intercalado en tu actividad lo ejecutará en el subproceso del controlador de eventos y bloqueará de forma efectiva el controlador de eventos. Esto retrasará el procesamiento de entrada y dará como resultado los cuadros de diálogo de ANR. Para evitar esto, mueve tus cálculos a un subproceso. En este documento sobre el diseño para la capacidad de respuesta, se analiza cómo hacerlo.
No sobrecargues una sola pantalla de actividad
Cualquier aplicación útil probablemente tendrá varias pantallas diferentes. Cuando diseñes las pantallas de tu IU, asegúrate de usar varias instancias de objetos Activity.
Según tu segundo plano de desarrollo, puedes interpretar que una actividad es similar a un applet de Java, ya que es el punto de entrada de tu aplicación. Sin embargo, eso no es del todo exacto: cuando una subclase Applet es el único punto de entrada para un applet de Java, una actividad debe considerarse como uno de los posibles puntos de entrada a tu aplicación. La única diferencia entre tu actividad "principal" y cualquier otra que puedas tener es que la "principal" resulta ser la única que expresó interés en la acción "android.intent.action.MAIN" en tu archivo AndroidManifest.xml.
Por lo tanto, cuando diseñes tu aplicación, piensa en ella como una federación de objetos Activity. Esto hará que tu código sea mucho más fácil de mantener a largo plazo y, como buen efecto secundario, también funciona bien con el historial de aplicaciones y el modelo de "pila de actividades" de Android.
Extiende temas del sistema
Cuando se trata de la apariencia de la interfaz de usuario, es importante integrarla bien. Los usuarios se asustan ante aplicaciones que contrastan con la interfaz de usuario que esperan. Cuando diseñes tus IU, debes intentar evitar hacer las tuyas tanto como sea posible. En su lugar, usa un tema. Puedes anular o extender las partes del tema que necesites, pero al menos comenzarás desde la misma base de IU que todas las demás aplicaciones. Para conocer todos los detalles, consulta Estilos y temas.
Diseña tu IU para trabajar con varias resoluciones de pantalla
Los distintos dispositivos con Android son compatibles con diferentes resoluciones de pantalla. Algunos incluso podrán cambiar las resoluciones sobre la marcha, por ejemplo, cambiando al modo horizontal. Es importante asegurarte de que tus diseños y elementos de diseño sean lo suficientemente flexibles como para mostrarse correctamente en una variedad de pantallas de dispositivos.
Afortunadamente, esto es muy fácil de hacer. En resumen, debes proporcionar diferentes versiones del material gráfico (si usas alguna) para las resoluciones clave y, luego, diseñar tu diseño para que se adapte a varias dimensiones. (Por ejemplo, evita usar posiciones hard-coded y, en su lugar, usa diseños relativos). Si haces eso, el sistema se encargará del resto y tu aplicación se verá genial en cualquier dispositivo.
Supón que la red es lenta
Los dispositivos Android incluirán una variedad de opciones de conectividad de red. Todos tendrán algún aprovisionamiento de acceso a los datos, aunque algunos serán más rápidos que otros. Sin embargo, el mínimo denominador común es GPRS, el servicio de datos que no es 3G para las redes GSM. Incluso los dispositivos compatibles con 3G pasarán mucho tiempo en redes que no sean 3G, por lo que las redes lentas seguirán siendo una realidad durante mucho tiempo.
Por eso, siempre debes codificar tus aplicaciones para minimizar el acceso a la red y el ancho de banda. No puedes suponer que la red es rápida, por lo que siempre debes planificar que sea lenta. Si tus usuarios están en redes más rápidas, eso es genial; su experiencia solo mejorará. Sin embargo, debes evitar el caso inverso: es probable que no sean populares las aplicaciones que se pueden usar en algunas ocasiones, pero que ralentizan el resto de forma frustrante en función de dónde se encuentre el usuario en un momento determinado.
Un posible problema aquí es que es muy fácil caer en esta trampa si estás usando el emulador, ya que este utiliza la conexión de red de tu computadora de escritorio. Es casi seguro que será mucho más rápida que una red móvil, por lo que te recomendamos que cambies la configuración del emulador que simule velocidades de red más lentas. Puedes hacerlo en Android Studio mediante el Administrador de AVD o mediante una opción de línea de comandos cuando inicias el emulador.
No supongas la pantalla táctil o el teclado
Android es compatible con una variedad de factores de forma de dispositivos. Es una forma sofisticada de decir que algunos dispositivos Android tendrán teclados "QWERTY" completos, mientras que otros tendrán configuraciones de 40 teclas, de 12 teclas o incluso otras. Del mismo modo, algunos dispositivos tendrán pantallas táctiles, pero muchos no.
Cuando crees tus aplicaciones, ten esto en cuenta. No hagas suposiciones sobre diseños de teclado específicos, a menos que estés realmente interesado en restringir tu aplicación para que solo pueda usarse en esos dispositivos.
Conserva la batería del dispositivo
Un dispositivo móvil no es muy móvil si está constantemente enchufado a la pared. Los dispositivos móviles funcionan con batería y, cuanto más tiempo dure esa batería con una carga, más felices estarán todos, especialmente el usuario. Dos de los mayores consumidores de energía de la batería son el procesador y la radio. Por eso, es importante que escribas las aplicaciones para que hagan el menor trabajo posible y usen la red con la menor frecuencia posible.
Minimizar la cantidad de tiempo del procesador que usa tu aplicación se reduce a escribir un código eficiente. Para minimizar el consumo de energía de la radio, asegúrate de procesar las condiciones de error correctamente y de obtener solo lo que necesites. Por ejemplo, no intentes realizar una operación de red constantemente si alguna falla. Si falló una vez, es probable que el usuario no tenga recepción, por lo que es probable que vuelva a fallar si lo intentas de inmediato. Lo único que harás es desperdiciar energía de la batería.
Los usuarios son bastante inteligentes: si tu programa necesita mucha energía, puedes estar seguro de que lo notarán. Lo único de lo que puedes estar seguro en ese momento es que tu programa no permanecerá instalado por mucho tiempo.