Como parte de nosso trabalho para modernizar as orientações sobre arquitetura de apps , queremos experimentar diferentes padrões de IU para ver o que funciona melhor, encontrar semelhanças e diferenças entre as alternativas e consolidar nossas descobertas como práticas recomendadas.
Para facilitar ao máximo a aplicação de nossas descobertas, precisávamos de uma amostra com um caso de negócios familiar e que não fosse muito complicada. E quem é que não conhece os apps TODO ? Escolhemos o Architecture Blueprints ! O Blueprints sempre serviu como um playground experimental para escolhas de arquitetura. E ele é ótimo para isso!
App Architecture Blueprints em ação Os padrões com os quais queremos fazer experiências são claramente afetados pelas diferentes APIs disponíveis atualmente. E a novidade são as APIs de estado do Jetpack Compose ! Como o Compose funciona perfeitamente com qualquer padrão de fluxo de dados unidirecional , vamos utilizá-lo para renderizar a IU e fazer uma comparação justa.
Esta postagem do blog conta como a equipe migrou o Architecture Blueprints para o Jetpack Compose . Como o LiveData também é considerado uma alternativa em nossos experimentos, deixamos a amostra no estado em que se encontrava no momento da migração. Nessa refatoração, as classes ViewModel e a camada de dados permaneceram inalteradas.
⚠️ A arquitetura usada nesta base de código baseada no LiveData não segue totalmente as práticas recomendadas de arquitetura mais recentes . Em particular, o LiveData não deve ser usado nas camadas de dados ou de domínios . Devem-se usar fluxos e corrotinas.
Agora que o contexto já ficou claro, vamos detalhar como abordamos a refatoração do Blueprints para o Jetpack Compose. Confira o código completo na ramificação dev-compose .
✍️ Planejamento de uma migração gradual Antes de realizar qualquer codificação real, a equipe criou um plano de migração para garantir que todos concordassem com as mudanças propostas. O objetivo final era que o Blueprints fosse um aplicativo de atividade única, com telas como funções que podem ser compostas e usando a biblioteca Compose Navigation recomendada para a movimentação entre as telas.
Felizmente, o Blueprints já era um app de atividade única que usava o Jetpack Navigation para a movimentação entre diferentes telas implementadas com fragmentos. Para migrar para o Compose, seguimos as orientações de interoperabilidade do Navigation, que recomendam que os apps híbridos usem o componente Navigation baseado em fragmentos para conter telas baseadas em visualização, telas do Compose e telas que usam tanto as visualizações quanto o Compose. Infelizmente, não é possível combinar os destinos dos fragmentos e do Compose no mesmo gráfico do Navigation.
O objetivo de uma migração gradual é facilitar as revisões de código e manter um produto em condições de entrega ao longo de toda a migração. O plano de migração envolvia três passos:
Migrar o conteúdo de cada tela para o Compose. Cada tela seria migrada individualmente para o Compose, incluindo seus testes de IU. Depois, os fragmentos se tornariam o contêiner/host de cada tela migrada. Migrar o app para o Navigation Compose, o que remove todos os fragmentos do projeto, e migrar a lógica da IU Activity para funções-raiz que podem ser compostas. Os testes completos também são migrados neste ponto. Remover as dependências do sistema de visualização. E foi isso o que fizemos! 🧑💻 Depois ⏩ de duas semanas, migramos as telas Statistics (PR ), Add/Edit task (PR ), Task detail (PR ) e Tasks (PR ) e mesclamos a PR final que migrou a lógica Activity e Navigation para o Compose, incluindo a remoção das dependências do sistema de visualização não utilizadas .
Como ocorreu a migração gradual do Blueprints para o Compose 💡 Destaques da migração Durante a migração, encontramos algumas peculiaridades específicas do Compose que valem destaque:
🧪 Testes de IU Assim que você começa a adicionar o Compose ao app, os testes que declaram IUs do Compose precisam usar APIs de teste do Compose .
Para os testes de IU no nível de tela , em vez de usar a API launchFragmentInContainer<FragmentType> , usamos a API createAndroidComposeRule<ComponentActivity> , que permite capturar recursos de strings nos testes. Esses testes são executados no Espresso e no Robolectric . Como o Compose já tem suporte para tudo isso, nenhuma mudança adicional foi necessária. Por exemplo, é possível comparar o código em AddEditTaskFragmentTest que foi migrado para AddEditTaskScreenTest . Observe que, se você usar o ComponentActivity, precisará contar com o artefato androidx.compose.ui:ui-test-manifest .
Nos testes completos ou de integração , também não tivemos nenhum problema. Graças à interoperabilidade do Espresso e do Compose , usamos as declarações do Espresso para verificar as visualizações e as APIs do Compose para verificar a IU do Compose. Aqui, podemos ver a aparência de AppNavigationTest em um ponto durante a migração para o Compose.
🤙 Eventos do ViewModel Tivemos problemas com o modo como os eventos do ViewModel eram manipulados no Blueprints. O Blueprints implementava uma solução de wrapper de evento para enviar comandos do ViewModel para a IU. No entanto, isso não funciona no Compose. Nossas orientações recentes recomendam a modelagem desses "eventos" como estado, e foi isso o que fizemos durante a migração.
Se observarmos o caso de uso do evento de exibição de mensagens na tela , substituímos o tipo Event<Int>do LiveData por "Int?". Isso também modela o cenário no qual não há mensagens a serem exibidas para o usuário. Nesse caso de uso em particular, o ViewModel também exige uma confirmação da IU sempre que a mensagem é exibida. Veja a diferença entre as duas implementações no seguinte código:
Embora isso possa parecer um trabalho adicional, à primeira vista, é uma garantia de que a mensagem seja exibida na tela.
No código da IU, a forma de assegurar que o evento seja manipulado apenas uma vez é fazer uma chamada para event.getContentIfNotHandled(). Essa abordagem funciona mais ou menos nos fragmentos, mas falha totalmente no Compose . Como as recomposições podem ocorrer a qualquer momento no Compose, o wrapper de evento não é uma solução válida. Se o evento for processado e a função for recomposta (algo que aconteceu bastante durante o teste dessa abordagem), o snackbar será cancelado, e o usuário poderá perder a mensagem. Esse é um problema de UX inaceitável! A solução wrapper de evento não deve ser usada em apps do Compose.
Veja o snippet de código a seguir com o antes (wrapper de evento) e o depois (evento como estado) do código. Como a exibição de mensagens na tela envolve a lógica de IU e as funções de tela que podem ser compostas estavam se tornando mais complexas, usamos uma classe detentora de estado simples para gerenciar essa complexidade (veja, por exemplo, AddEditTaskState ).
👌 Na dúvida, escolha a precisão do app Durante a refatoração, pode ser tentador migrar tudo para o Compose. Embora não haja nenhum problema nisso, você não deve sacrificar a experiência do usuário nem a precisão do app. A finalidade de fazer uma migração gradual é que o app esteja sempre em condições de entrega.
Isso aconteceu conosco durante a migração de algumas telas para o Compose. Não queríamos fazer um monte de migrações ao mesmo tempo, então migramos algumas das telas para o Compose antes da migração do wrapper de evento. Em vez de manipular o wrapper de evento no Compose e entregar uma experiência insatisfatória, continuamos manipulando essas mensagens no fragmento, enquanto o restante do código da tela estava no Compose. Veja, por exemplo, o estado de TasksFragment durante a migração .
🧐 Desafios Nem tudo correu tão bem quanto parecia. 🫤 Embora a conversão de conteúdo de fragmentos para o Compose seja direta, a migração dos fragmentos do Navigation para o Navigation Compose exigiu um pouco mais de tempo e raciocínio.
É necessário expandir e melhorar as orientações quanto a diferentes aspectos que tornarão a migração para o Compose mais simples no futuro. Esse trabalho provocou várias conversas, e esperamos ter novas orientações sobre isso em breve! 🎊
Por ser iniciante em Navigation ✋ e a pessoa que lidou com a migração para o Navigation Compose, enfrentei os seguintes desafios:
Nenhum código na documentação mostrava como navegar com argumentos opcionais ! Graças ao gráfico de navegação do Tivi , consegui me orientar e resolvi o problema (siga o problema registrado para a melhoria da documentação aqui ). A migração de um gráfico de navegação baseado em XML e de SafeArgs para o Kotlin DSL deveria ser uma tarefa mecânica e direta. Mas não foi tão fácil para mim, considerando-se que não trabalhei na implementação original. Ter um pouco de orientação sobre como fazer isso corretamente teria me ajudado bastante (siga o problema registrado para a melhoria da documentação aqui ).Mais do que um desafio, este é um ponto do tipo "te peguei!". A IU do Navigation já faz algumas coisas por você quando se trata da navegação. Como isso não existe no Compose, você precisa ficar de olho e fazer tudo manualmente. Por exemplo, manter limpa a pilha de retorno ao navegar entre as telas da gaveta requer NavigationOptions especiais (veja um exemplo aqui ). Isso já é coberto pela documentação , mas primeiro você tem que estar ciente de que precisa disso! 🧑🏫 Conclusões Em geral, a migração de fragmentos do Navigation para o Navigation Compose foi bem divertida! O mais engraçado é que gastamos mais tempo aguardando revisões de pares do que fazendo a migração do projeto em si! Criar o plano de migração e sincronizar todo mundo com certeza ajudou a definir as expectativas logo no começo e alertar os pares sobre futuras revisões demoradas.
Esperamos que você tenha gostado de ler sobre nossa abordagem da migração para o Compose, e queremos compartilhar em breve mais informações sobre os experimentos e as melhorias que faremos no Architecture Blueprints.
Caso tenha interesse em ver o Blueprints com o código do Compose, confira a ramificação dev-compose . E, caso deseje ver todas as PRs da migração gradual, esta é a lista:
👋
3 comentários :
One of the things I love about house of hazards is the surprise factor. You never know who's going to stumble next!
I wish I had found this earlier. Research on BISP Effectiveness It’s incredibly helpful!
geometry dash meltdown offers hundreds of unique skins for players to choose from, allowing for further customization of their character. Players can transform their cube into various forms, such as robots or spaceships, each with its own personalized interface. This variety adds depth to the gameplay and encourages players to experiment with different styles.
Postar um comentário