Migración de Jetpack Compose: mejores prácticas y estrategias | de Abhishek Saxena | diciembre 2022
Foto de Marc-Olivier Jodoin en Unsplash¿Tiene una aplicación que le gustaría migrar a Jetpack Compose? Para mí es el Pocket German Dictionary, una forma rápida y fácil de buscar significados y traducciones en alemán e inglés. A principios de este año, decidí para migrar la aplicación a Jetpack Compose, pero nunca completé la migración y recién publiqué los cambios en Play Store. Este artículo trata sobre mis experiencias, los desafíos que enfrenté, el proceso de pensamiento detrás de la migración y las decisiones de diseño que tomé. Si encuentra esto interesante o está pensando en migrar su aplicación a Compose, entonces este artículo es para usted y aprenda de mi increíble experiencia.
Contenidos
El estado de la aplicación antes de la migración
Antes de pasar al proceso de migración, me gustaría explicarle el estado inicial de la aplicación para que pueda comprender por qué tomé un determinado camino a lo largo del proceso. Como mencioné antes, este fue técnicamente mi primer proyecto personal en el mundo de Android después de aprender los conceptos básicos de los tutoriales. Quizás se pregunte por qué esto es tan importante para este artículo. 🤔Era un hola mundo sin patrón de diseño real o estándar para el desarrollo profesional. La aplicación era antigua o un poco anticuada para mí, pero todavía era relativamente nueva en términos de desarrollo de software. No se siguió ninguna arquitectura en la aplicación – NO MVVM o MVP, esto significa que puede encontrar todo el código para una actividad/pantalla dentro de la propia actividad y no dividirlo en diferentes clases. Sin mencionar que no se ha agregado ninguna biblioteca de inserción de dependencias para manejar las dependencias. No se siguieron principios de software. Sí, todo estaba en una sola clase. En resumen, la aplicación fue una gran bola de barro. Ahora estamos de acuerdo, así que hablemos de todo el proceso. Tenía dos grandes desafíos que superar.
- Migrar la base de código de Java a la base de código de Kotlin
- implementar la arquitectura de la aplicación.
Estos dos desafíos fueron los más grandes y la base de todas las decisiones que tomé. El primer desafío fue bastante fácil de completar, escribí todo el código nuevo en Kotlin y migré lentamente las clases de Java a las clases de Kotlin según fuera necesario. La segunda arquitectura de aplicaciones fue la más grande y tomó más tiempo. Migré el código base en tres fases:
- antes de la migración
- migración
- posmigración
Dividí el proceso en tareas más pequeñas y las repetí para cada pantalla.Tareas
- Limpie el código de espagueti en las actividades. (antes de la migración)
- Implementar la arquitectura MVVM. (antes de la migración)
- Configure la capa de interfaz de usuario para Jetpack Compose. (Migración)
- Migre la capa de la interfaz de usuario a Jetpack Compose. (Migración)
- La migración de la biblioteca/los componentes de la aplicación principal a Compose. (posmigración)
- Limpiar (después de la migración)
1. Código de espagueti limpio en actividades
Comprender los principios de diseño de SOLID fue la fuerza impulsora detrás de esta tarea. Comencé una actividad y anoté las diferentes responsabilidades que cubría la actividad (principio de responsabilidad única). Luego dividí la clase en varias clases hasta separar las responsabilidades. Repetí este proceso para todas las actividades. Una cosa de la que me aseguré fue No he refactorizado el código relacionado con la interfaz de usuario o el marco de trabajo de Android. ya que quería eliminarlo en los próximos pasos cuando haría la migración de redacción. Si es así, rómpelo hasta que no pueda más. Simple.
2. Implementar la arquitectura MVVM
MVVM es la arquitectura de aplicación recomendada para las aplicaciones de Android. Así que también decidí actualizar la aplicación con el mismo. Una vez más, me aseguré de realizar solo los cambios mínimos en la capa de la interfaz de usuario al implementar la arquitectura. Antes de comenzar a trabajar en este paso, configuré la biblioteca de inyección de dependencias de Hilt en la aplicación para administrar las dependencias. Aparte de la capa de la interfaz de usuario (Actividades/VM), actualicé la capa del Repositorio para seguir el patrón del paso anterior. La mayoría de las veces tuve que agregar ViewModel para manejar la lógica de la interfaz de usuario y actualizar la actividad de forma reactiva con los cambios. Este paso fue bastante fácil ya que no tenía mucha lógica en mi aplicación, así que principalmente uní la capa de datos con la capa de UI en ViewModels. Durante este paso, también rediseñé la estructura del paquete de la aplicación para el futuro y empaqueté el código según las funciones en lugar del tipo de clase/capa:El principal beneficio de este enfoque fue que para cuando planee modularizar la aplicación, ya tengo la mayor parte del trabajo hecho debido a la estructura de los paquetes.
3. Configure la capa de interfaz de usuario para Jetpack Compose
Este paso fue diferente, ya que hay varias formas de actualizar la interfaz de usuario en Jetpack Compose. Entonces, ¿cómo puedo hacerlo? Algo que leí en el servidor de discordia r/androiddev que me ayudó mucho, a saber:
Su ViewModel debe ser independiente de la capa de la interfaz de usuario y desconocer los detalles de implementación de la interfaz de usuario.
Cuando lo piensa, esta es una declaración brillante que es realmente perspicaz al diseñar sus aplicaciones. Permítanme explicar brevemente cómo entendí la declaración anterior. Debe escribir ViewModels de tal manera que no dependan de cómo se implementa la interfaz de usuario. Básicamente, esto significa que su ViewModel no debe saber si la interfaz de usuario está escrita en XML o Compose, o si ViewModel es parte de una aplicación de Android, una aplicación web o una aplicación multiplataforma. ViewModel debe tener dependencias mínimas en el marco y debe saber lo menos posible sobre el marco al que pertenece. Una vez que haya adoptado este principio y haya escrito su código de esta manera, puede cambiar fácilmente la implementación de la interfaz de usuario de XML a Compose o cualquier otro conjunto de herramientas de interfaz de usuario con la cantidad mínima de cambios. De hecho, puede migrar su aplicación a KMM o KMP con cambios mínimos y compartir ViewModel en este marco. Solo quería mencionar esto para que pueda pararse y pensar de manera diferente mientras trabaja con ViewModels o cualquier otra parte de la aplicación. Ahora volvamos al proceso de migración. Elijo almacenar el ViewState de cada pantalla en una clase Kotlin sellada con diferentes estados como Inicial, Cargando, Cargado y Error, que son estados comunes para la mayoría de los casos de uso. La lista de estados no es fija, ya que puede variar con la complejidad de la pantalla y depende del estado en el que se encuentre la pantalla. Se decidió la pantalla, delegué los datos a ViewState y ViewModel actuó como un puente entre el estado de la interfaz de usuario y eventos de la interfaz de usuario, mientras que ViewState se convirtió en la única fuente de verdad para la capa de la interfaz de usuario. En este punto, se realizó la mayor parte del trabajo y, al final de este paso, la aplicación estaba bien estructurada y ViewModel no dependía de cómo se implementó la interfaz de usuario.
4. Migra la capa de la interfaz de usuario a Jetpack Compose
Este paso fue fácil y difícil al mismo tiempo. Verá, cuando comencé el proceso de migración, aprendí Jetpack Compose, lo que significa que me atasqué mucho y, gracias a la maravillosa comunidad, pude superar los obstáculos que encontré en el camino. Una de las mejores cosas de Compose es su interoperabilidad, lo que significa que puede tener la interfaz de usuario de Compose en XML y viceversa. Esto ayudó mucho, seguí los pasos a continuación en esta tarea:
- Divide la pantalla en componentes del tamaño de una unidad.
- Vuelva a escribir los componentes de talla única en Compose.
- (Opcional) Agregue el componente creado anteriormente a la vista.
- Repita este proceso hasta que se reescriba toda la pantalla en Redactar.
Tomemos el ejemplo de una pantalla simple que muestra una lista de estudiantes para comprender el proceso anterior. El componente de tamaño de unidad en una lista que se muestra con RecyclerView es el elemento UI o ViewHolder. Abrevie las mejores prácticas para mostrar cómo dividir la pantalla en elementos componibles sin estado de tamaño unitario, que luego se integran en el elemento componible con estado que interactúa con ViewModel. Puede crear un StudentItem.kt que se implementará en Compose. A continuación, cree un componente de pantalla sin estado que contenga la lista y el elemento de la lista. Este archivo StudentListContent que admite composición sin estado está integrado con StudentListScreen, que es un archivo que admite composición con estado. Ahora hablemos del paso opcional donde brilla la interoperabilidad. Para algunas pantallas, la interfaz puede ser compleja y puede resultarle difícil migrar toda la pantalla a Redactar a la vez. En situaciones como esta, puede seguir los dos primeros pasos creando componentes del tamaño de una unidad y migrando lentamente toda la pantalla. Puede leer más sobre el proceso en los documentos oficiales de estrategia de migración de Android Compose. Una vez que toda la pantalla se haya migrado a unidades componibles de talla única, cree la pantalla componible y vuelva a escribir la pantalla con Compose para completar el proceso de migración. Algo que me ayudó mucho durante este proceso fue que escribí la pantalla sin estado para minimizar la dependencia de la pantalla de la arquitectura y el marco subyacentes. Esto también hizo que la pantalla fuera comprobable y escalable. Luego creé un componible con estado que actúa como un puente entre ViewModel y la pantalla sin estado, como se muestra en el ejemplo anterior. Repita el paso para las pantallas/actividades restantes. En este punto, su capa de interfaz de usuario debe escribirse usando Compose.Pero espere, la migración aún no está completa, hay dos pasos más. Veamos cuáles son esos.
- Migre bibliotecas/componentes de aplicaciones principales a Compose.
- Elimina y limpia archivos innecesarios.
5. La migración de la biblioteca/los componentes de la aplicación central a Compose
Este paso incluye los pasos para migrar la migración de arquitectura a nivel de aplicación y la migración de biblioteca. Aunque las pantallas estaban escritas en Compose, en ese momento estaban en cualquier actividad. Así que eliminé la navegación de la aplicación en Compose usando la biblioteca de destino de Compose y configuré actividades. Después de eliminar las actividades, me quedé con MainActivity, una sola actividad en toda la aplicación con todas las diferentes pantallas implementadas en Compose. Luego migré los recursos a Compose, estos recursos eran colors.xml, values.xml, themes.xml y fonts.xml. Migré todos los componentes que podían migrarse a Compose para crear una aplicación de Compose 100 % pura. Es posible que algunos lectores necesiten reescribir MainActivity para acomodar los cambios y migrar otros componentes principales de la pantalla como DrawerLayout, Bottom Navigation, etc., pero ese no fue mi caso.
6. Elimina y limpia archivos innecesarios
Como sugiere el nombre, eliminé los archivos XML sobrantes, como los recursos dibujables, los archivos XML relacionados con el menú y más. Por último, refactoricé/limpié las partes sobrantes de la aplicación, como AndroidManifest, para que solo contuviera MainActivity.