Recientemente, hemos redise帽ado por completo la aplicaci贸n Pyrus para Android. La primera versi贸n de la aplicaci贸n ya funcionaba con Android 2.2. Al negarnos a admitir Android por debajo de 4.1, pudimos pagar la deuda t茅cnica acumulada y simplificamos significativamente el c贸digo fuente. S铆, perdimos una parte de los usuarios (menos del 1%), pero por otro lado, ahorramos tiempo a los desarrolladores para corregir errores raros. Podremos invertirlo en el desarrollo de funcionalidades para todos los usuarios actuales y nuevos. A la larga, esto es mucho m谩s importante.
Aqu铆 compartimos experiencias que pueden ser 煤tiles para aquellos que est谩n considerando comenzar a desarrollar la plataforma Android.
Android atrae buenas herramientas de desarrollo, lenguaje probado en el tiempo (Java) con la sintaxis habitual, una gran audiencia de usuarios. Sin embargo, crear aplicaciones convenientes en Android es dif铆cil: a pesar de la API desarrollada, a menudo sucede que no hay un componente listo para usar con un comportamiento adecuado. Debe abandonar las ideas del dise帽ador de experiencia de usuario, aceptar depender de bibliotecas de terceros o escribir usted mismo. No todas las soluciones arquitect贸nicas en Android resultaron exitosas, y esto tambi茅n aumenta la cantidad de c贸digo sin un beneficio obvio.
Muriendo y resucitando de las cenizas
La interfaz en Android se basa en la actividad. Este es un contenedor que generalmente ocupa toda la pantalla del dispositivo, otros widgets viven en 茅l y recibe eventos sobre la interacci贸n del usuario.
Cuando el usuario activa la aplicaci贸n, se genera un evento onResume en la Actividad. Presion茅 el bot贸n de Inicio - La actividad desapareci贸, pero antes de eso recibi贸 el evento onPause. Hasta ahora, todo es l贸gico. 驴Qu茅 sucede cuando un usuario gira la pantalla del dispositivo 90 grados? Nunca adivinar谩s: el sistema operativo mata la actividad y se vuelve a crear. Cuando desinstala una actividad, todos los componentes que contiene se eliminan. Esto significa que debe escribir un c贸digo especial que guarde el estado de los widgets dentro de la Actividad (posici贸n de desplazamiento, texto seleccionado, estado de las casillas de verificaci贸n) y los restaure despu茅s de que se vuelva a crear el padre. Android hace algo de este trabajo por ti, pero no todo.
Es dif铆cil entender qu茅 llev贸 a los arquitectos a tomar esa decisi贸n. M谩s tarde, alguien decidi贸 que se ve铆a feo y agreg贸 el mismo par谩metro, que suprime el comportamiento predeterminado del sistema. Ahora, en lugar de volver a crear Actividad, el SO puede llamar al m茅todo especial onConfigurationChanged. Esta "soluci贸n" parece un truco sucio y solo exacerb贸 el problema, porque la documentaci贸n de Android
dice "esta t茅cnica deber铆a considerarse una medida extrema y no se recomienda para la mayor铆a de las aplicaciones".
Fragmentos Fragmentados
Inicialmente, el sistema operativo Android fue creado para tel茅fonos. Con el advenimiento de las tabletas, el equipo de Android decidi贸 acertadamente que los elementos de la interfaz que anteriormente estaban separados por diferentes actividades debido al peque帽o tama帽o de la pantalla del tel茅fono ahora pueden coexistir en la pantalla grande de la tableta. (Ejemplo: la lista de cartas y el correo electr贸nico en el tel茅fono estaban en dos Actividades diferentes, y en la tableta compartieron una pantalla juntos). S铆, el problema es que varias Actividades no pueden interactuar simult谩neamente con el usuario por dise帽o. Por lo tanto, se necesita otro mecanismo para reutilizar componentes, y se encontr贸 una soluci贸n: aparecieron fragmentos (Fragmento).
Por
definici贸n , un Fragmento representa un comportamiento o parte de una interfaz de usuario en una Actividad. Todo esta claro. Sin embargo, en las instrucciones para usar fragmentos encontramos la secci贸n "Agregar un fragmento que no tiene una interfaz de usuario". Que? 隆Resulta que un fragmento puede no tener su propia ventana para renderizar!
Pero, 驴qu茅 sucede con los fragmentos al girar la pantalla? Los desarrolladores de Android pensaron en esto: cuando recrea una actividad, todos sus fragmentos tambi茅n se recrean autom谩ticamente. Sin embargo, tras una cuidadosa consideraci贸n, el fragmento tiene el m茅todo "setRetainInstance", si lo llama, este fragmento no se eliminar谩 ni restaurar谩 durante los turnos.
Esbelto y consistente, este concepto no puede ser llamado.
Operaci贸n asincr贸nica
El subproceso de procesamiento de eventos no puede bloquearse mediante operaciones largas, por lo tanto, el intercambio de datos con el disco y la red debe realizarse de forma asincr贸nica en otros subprocesos. Android proporciona hasta cuatro mecanismos para la operaci贸n asincr贸nica: AsyncTask, Service, IntentService, Thread. Se alienta a los desarrolladores a determinar cu谩l es el mejor para ellos. La elecci贸n no es trivial: por ejemplo, si la aplicaci贸n inici贸 AsyncTask y el usuario gir贸 la pantalla durante su ejecuci贸n, al finalizar el trabajo, AsyncTask no puede encontrar una nueva Actividad (creada despu茅s del turno). Y ninguno de los cuatro mecanismos implementa una l贸gica simple: si el usuario inici贸 el segundo proceso asincr贸nico sin esperar los resultados del primero, y luego el primero todav铆a termin贸 antes, entonces es apropiado ignorar y no reflejar sus resultados en la interfaz de usuario.
Se hace evidente la aparici贸n de numerosas bibliotecas: buses de datos para organizar el trabajo asincr贸nico dentro de una aplicaci贸n de Android.
Reiniciar la aplicaci贸n despu茅s de OOM
Si Android se queda sin RAM, el sistema operativo puede completar cualquier proceso. Cuando el usuario active la aplicaci贸n en el futuro, el sistema intentar谩 restaurar su estado. En teor铆a, esto deber铆a ser invisible para el desarrollador (si implement贸 el m茅todo onSaveInstanceState en todos los componentes): el propio sistema recrea la pila de Actividad tal como estaba durante la parada forzada. Sin embargo, si la inicializaci贸n lleva tiempo (por ejemplo, cargar cach茅s desde el disco o desde la red), ser谩 correcto mostrarle al usuario un indicador de espera. Resulta que al crear una actividad, a煤n necesita monitorear el "arranque en fr铆o" y llevar a cabo la inicializaci贸n manualmente. Sin mencionar que al reiniciar, no se restauran Thread y AsyncTask.
驴Qu茅 posibilidades hay de quedarse sin memoria en la pr谩ctica? T铆picamente, esta situaci贸n ocurre cuando se procesan im谩genes de alta resoluci贸n. Por ejemplo, una imagen de 2048x1536 ocupa 12Mb en la memoria de un objeto de mapa de bits. La cantidad de memoria disponible para la aplicaci贸n depende del modelo de dispositivo espec铆fico y, a veces, es muy peque帽a (64 Mb o 128 Mb). Dado que el recolector de basura en todas las versiones de Android anteriores a 8.0 no estaba compactando, incluso si tiene 100Mb de memoria libre, pero est谩 dividido en bloques de 10Mb, un intento de asignar memoria para esa imagen condujo a un bloqueo de la aplicaci贸n.
Recogida de basura eterna
Una vez que nuestro usuario se dio cuenta de que al desplazarse por las listas largas, la aplicaci贸n "se retrasaba": cada 2-3 segundos la animaci贸n se deten铆a por breves pausas (200-300 ms), y luego continuaba. El an谩lisis mostr贸 que la recolecci贸n de basura a menudo comienza sospechosamente en la aplicaci贸n. Result贸 que la clase HashMap est谩ndar (que usamos para obtener un objeto Java por su ID) es ineficiente en nuestro caso: necesitamos crear un objeto contenedor para cada clave, que es un entero (int). Por lo tanto, el n煤mero de asignaciones de memoria aumenta sin ning煤n beneficio. La soluci贸n fue cambiar a un contenedor especial SparseArray (disponible solo en Android, no en la plataforma est谩ndar de Java), lo que inmediatamente redujo la presi贸n sobre el recolector de basura.
驴Qu茅 tiene que ver el retraso en la animaci贸n? El hecho es que el recolector de basura en Android detuvo todos los hilos durante su funcionamiento, incluido el hilo principal involucrado en el renderizado. En las versiones de Android que comienzan con 5.0, se usa otra m谩quina virtual (ART en lugar de Dalvik) y un algoritmo diferente de recolecci贸n de basura, que detiene cada vez menos la animaci贸n. (Puede leer sobre c贸mo comparar el tiempo de recolecci贸n de basura
aqu铆 ).
Ver documentos
Si desea insertar vistas previas de documentos en su aplicaci贸n, se ve obligado a decepcionar: en Android no hay un componente integrado que pueda mostrar archivos de Word, Excel, Powerpoint. Sin mencionar los archivos ZIP o documentos PDF. Salida? Obligue al usuario a instalar aplicaciones de terceros para ver cada tipo de archivo o usar el componente WebView (en realidad un navegador). En ambos casos, cuando el usuario graba el archivo, se iniciar谩 una aplicaci贸n externa y algunos escenarios simplemente no se pueden implementar, por ejemplo, una alimentaci贸n de im谩genes: no hay una manera f谩cil de integrar WebView en un ViewPager est谩ndar.
Que sigue
Algunos estudios afirman que la
complejidad del desarrollo de Android es relativamente alta . Nuestra experiencia muestra que es f谩cil para un desarrollador competente acostumbrarse a las caracter铆sticas de esta plataforma. Y aunque hoy Android es el
sistema operativo m贸vil m谩s popular del mundo , en 5-10 a帽os la situaci贸n puede cambiar radicalmente.
En los 煤ltimos a帽os, Google ha estado desarrollando en secreto el
nuevo Fuchsia OC con un modelo de seguridad fundamentalmente diferente (en comparaci贸n con el sistema operativo moderno). Probablemente usar谩
Dart como el lenguaje de programaci贸n principal y el marco
Flutter como la forma principal de crear aplicaciones. Se rumorea que
Fuchsia puede reemplazar Android y ChromeOS en todos los dispositivos. Si Google hace esto, es probable que la compa帽铆a brinde soporte nativo para aplicaciones escritas para Android (como lo hizo Microsoft al cambiar de DOS a Windows). Por lo tanto, hasta ahora no puede preocuparse y continuar acumulando experiencia en Android. Y para aquellos que quieran mirar hacia el futuro, pueden descargar Flutter y jugar con 茅l
aqu铆 .