Diseño sin interrupciones

Incluso si tu aplicación es rápida y tiene capacidad de respuesta, ciertas decisiones de diseño aún pueden causar problemas a los usuarios debido a interacciones no planificadas con otras aplicaciones o cuadros de diálogo, pérdidas involuntarias de datos, bloqueos no intencionales, etc. Para evitar estos problemas, es útil comprender el contexto en el que se ejecuta tu aplicación 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 interrupción es cuando el proceso en segundo plano de una aplicación, por ejemplo, un servicio o un receptor de emisión, abre una ventana emergente con un cuadro de diálogo en respuesta a algún evento. Esto puede parecer un comportamiento inofensivo, especialmente cuando estás compilando y probando 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 muestre el cuadro de diálogo. Por lo tanto, tu aplicación podría mostrar su cuadro de diálogo detrás de la aplicación activa o podría enfocar desde la aplicación actual y mostrar el cuadro de diálogo delante de lo que estaba haciendo el usuario (por ejemplo, mientras marcaba para una llamada telefónica). Ese comportamiento no funcionaría para tu aplicación o para el usuario.

A fin de evitar estos problemas, tu aplicación debe usar la instalación del sistema adecuada para notificar al usuario: las clases Notification. Mediante el uso de notificaciones, tu aplicación puede indicarle al usuario que se ha producido un evento mostrando un ícono en la barra de estado en vez de enfocar e interrumpir al usuario.

Otro ejemplo de un problema de interrupción es cuando una actividad pierde accidentalmente el estado o los datos del usuario porque no implementa correctamente onPause() y otros métodos de ciclo de vida. O bien, si tu aplicación expone datos destinados a otras aplicaciones, debes exponerlos a través de un ContentProvider en vez de hacerlo, por ejemplo, a través de una base de datos o un archivo sin formato 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 combinados flexibles en vez de fragmentos de código de caja negra. Como desarrollador, esto te permite ver todo el sistema como una federación aún más grande de estos componentes. Esto te beneficia, ya que te permite lograr una integración correcta y sin interrupciones con otras aplicaciones, por lo que debes diseñar tu propio código para devolverle 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, tu aplicación probablemente perderá esos datos cuando se cierre, a menos que primero guardes el trabajo en progreso. El "estilo Android" es hacer exactamente eso: las aplicaciones para Android que aceptan o editan entradas deben anular el método onSaveInstanceState() y guardar su estado de alguna manera apropiada. 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

Al igual que, seguramente, no estarías dispuesto a exponer tu cuerpo en la calle, tampoco deberías exponer tus datos. Si bien es posible exponer ciertos tipos de aplicaciones para que todos los usuarios puedan leer, esta no suele ser la mejor idea. La exposición de 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 haya actualizado 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 permita mantenimiento. Usar un ContentProvider es muy parecido a insertar una interfaz de lenguaje Java para dividir y estructurar en componentes dos partes de código estrechamente acopladas. Esto 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 hace a propósito. Por eso, debes evitar las actividades de generación, excepto en respuesta directa a la entrada 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 actualmente y el usuario se molestará. Tal vez aún peor: tu actividad quizá 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 vez de generar IU de actividad directamente desde el segundo plano, debes usar NotificationManager para configurar notificaciones. Estas aparecerán en la barra de estado y el usuario podrá hacer clic en ellas cuando lo desee para ver lo que tu aplicación debe 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 debe realizar un procesamiento costoso o de larga duración, probablemente deberías moverlo a un subproceso. Esto evitará que el temido cuadro de diálogo "La aplicación no responde" se muestre al usuario, con el resultado final de la desaparición feroz de tu aplicación.

De forma predeterminada, todo el código en 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 necesita sacar de la cola ese evento y procesarlo rápidamente; si no lo hace, el sistema concluye unos segundos de que la aplicación se cuelga y ofrece cerrarla para el usuario.

Si tienes un código prolongado, activarlo intercalado en tu actividad lo ejecutará en el subproceso del controlador de eventos, lo que efectivamente bloqueará el controlador. Esto retrasará el procesamiento de entrada y dará como resultado los cuadros de diálogo ANR. Para evitarlo, mueve tus procesamientos a un subproceso. En este documento, Cómo diseñar para capacidad de respuesta, se explica 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 una actividad como algo similar a un applet de Java, ya que es el punto de entrada para 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.

Entonces, 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 "backstack" de Android.

Extiende temas del sistema

Cuando se trata de la apariencia de la interfaz de usuario, es importante combinar bien. Los usuarios se asustan ante aplicaciones que contrastan con la interfaz de usuario que esperan. Cuando diseñes tus IU, debes intentar evitar que contrasten tanto como sea posible. En su lugar, usa un tema. Puedes anular o extender las partes del tema que necesitas, pero al menos comienzas 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, como cambiar al modo de paisaje. Es importante que te asegures 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.

Por suerte, esto es muy fácil de hacer. En resumen, debes proporcionar diferentes versiones de tu material gráfico (si usas alguno) para las resoluciones clave y, luego, crear tu diseño para acomodar varias dimensiones. (Por ejemplo, evita usar posiciones hard-coded y, en su lugar, usa diseños relacionados). Si lo haces, el sistema se encargará del resto y tu aplicación se verá excelente 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 no 3G para redes GSM. Incluso los dispositivos con 3G pasarán mucho tiempo en redes que no serán 3G, por lo que las redes lentas seguirán siendo una realidad durante bastante 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 planear que sea lenta. Si tus usuarios están en redes más rápidas, eso es excelente: su experiencia solo mejorará. Sin embargo, querrás evitar el caso inverso: es probable que no sean populares las aplicaciones que se pueden usar en algunas ocasiones, pero que retrasan 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 usa la conexión de red de tu computadora de escritorio. Es casi seguro que será mucho más rápida que una red celular, por lo que querrás cambiar la configuración del emulador que simula 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 al iniciar 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 elegante 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 otros 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 que digamos si está constantemente enchufado a la pared. Los dispositivos móviles funcionan con baterías y, cuanto más tiempo dure la batería con una carga, más felices estarán todos, en especial 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 tus 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 falla alguna. Si falló una vez, probablemente se deba a que el usuario no tiene recepción. En ese caso, es probable que vuelva a fallar si lo intentas de inmediato, y solo consumirás 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 de que tu programa no permanecerá instalado por mucho tiempo.