Usamos diariamente uma variedade de aplicativos em dispositivos como celulares, relógios, TVs e computadores, inseridos em um amplo ecossistema digital.
Essa diversidade de plataformas exige estratégias de desenvolvimento que proporcionem atualizações simultâneas e experiências de usuário uniformes.
Neste artigo, exploraremos o Kotlin Multiplataforma (KMP) e como ele se compara com outras tecnologias cross-plataforma, como React Native e Flutter. Discutiremos as vantagens e desafios dessas abordagens, oferecendo visões úteis para devs que buscam soluções eficientes para desenvolvimento multiplataforma.
O que é desenvolver "nativo"?
Desenvolvimento nativo é a criação de aplicativos feitos para operar especificamente em uma plataforma, como Android, iOS, desktop, web, tirando proveito de todas as suas capacidades.
Aplicativos nativos se integram perfeitamente com o hardware e seguem as diretrizes de design da plataforma, resultando em interfaces responsivas e acesso imediato às últimas atualizações do sistema.
Para cada plataforma, os fabricantes fornecem um SDK (Kit de Desenvolvimento de Software) que facilita a criação de aplicações dedicadas.
Contudo, o desenvolvimento nativo implica desafios, como:
- Necessidade de se adaptar a diferentes ambientes e linguagens
- Gerenciar múltiplas base de código
- Lidar com a fragmentação de dispositivos, como tamanhos de tela e versões de sistema variados
- Requer atenção constante a novas atualizações dos sistemas operacionais
- Retrocompatibilidade para garantir o funcionamento em versões antigas
A complexidade aumenta com a necessidade de dominar ferramentas e APIs específicas, resultando em uma manutenção mais trabalhosa.
Introduzindo frameworks cross-plataforma
Frameworks cross-plataforma como React Native e Flutter apresentam um SDK próprio, que pode atuar como uma camada adicional sobre o SDK nativo.
É inegável a ascensão dessa solução no ecossistema de aplicativos. Usando dados do Flutter:
- Em 2021, Flutter representava 3.2% do total, contando com mais de 150.000 dos 4,67 milhões de aplicativos na Play Store [1, 2].
- No terceiro trimestre de 2022, Flutter representava cerca de 14.1% contando com mais de 500.000 dos 3,55 milhões aplicativos publicados na Play Store [1, 2].
- Em novembro de 2023, Flutter conta com cerca de 35% contando com 1 milhão dos 2,87 milhões de aplicativos disponíveis na Play Store [1, 2].
A demanda por soluções cross-plataforma vem do desejo de simplificar o complexo processo de desenvolvimento de aplicativos para múltiplas plataformas.
A necessidade de dominar linguagens e SDKs diferentes para cada plataforma, como Kotlin para Android e Swift para iOS, além das constantes atualizações tecnológicas, impõe um grande desafio ao longo prazo.
Frameworks cross-plataforma, como Flutter e React Native, oferecem um caminho mais eficiente, permitindo o uso de um único código-base para várias plataformas, economizando tempo e esforço significativos.
Introduzindo o React Native
React Native é um framework de código aberto que conecta o JavaScript e React com componentes nativos para Android e iOS.
Essa metodologia é especialmente conveniente para devs com experiência no universo Web/React.
- Um componente
Text
no React Native é convertido em umUITextView
no iOS. - No Android, o mesmo componente
Text
se torna umTextView
.
Atualmente, o React Native possuí 2 tipos de arquiteturas: uma atual e a nova.
Arquitetura atual do React Native
A arquitetura atual e estável do React Native é baseada em três threads principais:
- Thread do JavaScript: Responsável pela execução do código JavaScript.
- Main Thread Nativa: Ou "thread principal", gerencia a UI e as interações do usuário.
- Thread de Background (Shadow Node): Administra a criação e manipulação dos nodes.
A comunicação entre o JavaScript e o código nativo é realizada via uma "ponte", que funciona como um terminal de transmissão de dados, permitindo a desserialização e execução das operações necessárias.
Desafios da Arquitetura Antiga
- Assincronicidade: a ponte opera de forma assíncrona, causando possíveis atrasos na atualização da UI quando a espera não é necessária.
- Execução Single-threaded do JavaScript: restringe todas as computações a uma única thread, podendo causar bloqueios e atrasos em operações intensivas.
- Overheads de serialização: a transferência de dados entre as camadas requer serialização (geralmente em JSON) e desserialização, adicionando sobrecarga computacional e afeta o desempenho.
A Nova arquitetura do React Native
A nova arquitetura do React Native foca em melhorar a comunicação entre as threads, eliminando a necessidade de serialização/desserialização e utilizando múltiplas threads para aprimorar o desempenho.
⚠️ Fase experimental
Essa nova arquitetura ainda é experimental e está sujeita a mudanças à medida que o projeto evolui.
É importante estar ciente de que a implementação atual inclui várias etapas manuais e não reflete a experiência final de desenvolvimento prevista para a arquitetura renovada.
Principais componentes da nova arquitetura
- Fabric: Uma reescrita total da camada de renderização, otimizando a interação entre JavaScript e código nativo. O Fabric elimina a necessidade de serialização e desserialização, permitindo atualizações de UI imediatas e animações mais fluidas, reduzindo simultaneamente a carga computacional geral.
- JSI (JavaScript Interface, uma interface em JavaScript para código nativo): Substitui a ponte tradicional, oferecendo uma camada de abstração mais leve que permite chamadas sincronizadas entre JavaScript e código nativo.
- TurboModules: Módulos otimizados que usam o JSI para um acesso mais direto e eficiente.
- React Renderer: Um novo renderizador que colabora com o JSI para melhorar o desempenho da UI.
Turbo Modules
Anteriormente, a comunicação no React Native entre as camadas Nativa e JavaScript era realizada através da ponte JavaScript, ou os "Native Modules".
Os Turbo Modules representam uma evolução significativa dos NativeModule
no React Native, abordando desafios como a inicialização prematura e a serialização de dados.
- Carregamento preguiçoso: permitem o carregamento preguiçoso de módulos, acelerando a inicialização do aplicativo.
- Comunicação direta: Ao evitar a JavaScript Bridge e comunicar-se diretamente com o código nativo, reduzem a sobrecarga de comunicação entre o JavaScript e o código nativo.
- Codegen para tipagem segura: O Codegen gera uma interface JavaScript no momento da construção, garantindo que o código nativo permaneça sincronizado com os dados provenientes da camada JavaScript.
- Uso de JSI: As ligações JSI (JavaScript Interface) possibilitam uma interação eficiente e direta entre JavaScript e código nativo sem a necessidade da ponte, proporcionando uma comunicação mais rápida e otimizada.
O Fabric aproveita das capacidades dos Turbo Modules e do Codegen. Juntos, esses componentes formam os pilares da nova arquitetura no React Native, oferecendo desempenho aprimorado e interoperabilidade mais eficiente entre código nativo e JavaScript.
🔗 Exploring React Native's new architecture
Introduzindo o Flutter
Flutter é um kit de desenvolvimento de interface de usuário (UI toolkit e framework), de código aberto, criado pela empresa Google em 2015, baseado na linguagem de programação Dart, que possibilita a criação de aplicativos compilados nativamente, para os sistemas operacionais Android, iOS, Windows, Mac, Linux, Fuchsia e Web.
Do ponto de vista arquitetural, o Flutter possui três camadas – o framework, a engine e a plataforma – e se baseia em especificidades da linguagem Dart, como a compilação ahead-of-time (AOT).
Como dev, você interage principalmente com o framework, escrevendo o aplicativo e os widgets (componentes da UI no Flutter) de maneira declarativa usando Dart.
A engine, então, renderiza isso em uma tela usando o Skia, que é posteriormente enviada às plataformas nativas: Android, iOS ou web. A plataforma nativa apresenta o canvas e envia os eventos que ocorrem de volta:
Flutter vs React Native
A transição do React Native para o Flutter revela uma evolução no desenvolvimento cross-plataforma. Enquanto o React Native oferece um caminho eficiente com JavaScript, o Flutter se destaca com a flexibilidade do Dart, uma linguagem otimizada para UIs interativas.
Embora a arquitetura do Flutter seja semelhante à do React Native, há uma diferença significativa em termos de desempenho.
Um dos componentes-chave que permite ao Flutter alcançar um desempenho superior ao do React Native é a integração mais profunda com o lado nativo, o que significa que ele não usa os SDKs tradicionais.
Em vez disso, o Flutter utiliza o Android NDK e o LLVM do iOS para compilar o código C/C++ que vem do engine.
Com a nova arquitetura do React Native, essa diferença de desempenho poderá ficar menos expressiva.
Desvantagens do Flutter
Embora o Flutter tenha um desempenho satisfatório, superando o React Native em termos de compilação de Dart para código nativo, ele enfrenta desafios como o tamanho aumentado dos aplicativos, devido à inclusão de seu motor de execução e widgets.
Além disso, a extensão de funcionalidades não suportadas nativamente pelo Flutter exige a comunicação entre Dart e as linguagens nativas por meio de canais e estruturas de dados específicas, o que pode ser uma solução menos eficiente e mais complexa em comparação com a interoperabilidade entre Java e Kotlin ou Objective-C e Swift.
🔗 Android & iOS native vs. Flutter vs. Compose Multiplatform
O desafio do Dart no Flutter
Como toda linguagem, Dart impõe um desafio natural de aprendizado e aplicação.
Embora o Dart seja uma linguagem moderna e dinâmica, é comum que devs de outras plataformas nativas possam encontrar uma barreira ao adentrar neste novo ecossistema, como funcionalidades específicas de linguagens como Kotlin ou Swift.
Dart está constantemente se aprimorando e, embora possa não ter a mesma percepção de maturidade que linguagens estabelecidas, ela oferece uma série de recursos interessantes que estão ganhando reconhecimento na comunidade de desenvolvimento.
Considerações finais sobre cross-plataforma
As soluções cross-plataforma abstraem as complexidades nativas, permitindo escrever um único código para diversos dispositivos.
Porém, é comum encontrar limitações ao se integrar com a plataforma nativa, impactando o desempenho e a experiência da aplicação.
Além disso, a adaptação a atualizações das plataformas pode ser lenta, pois o framework cross-plataforma precisa ser atualizado para suportar novas funcionalidades nativas.
Introduzindo o Kotlin Multiplataforma (KMP)
O KMP se destaca na integração com plataformas nativas. Esta abordagem permite devs compartilhem a lógica de negócios mantendo as interfaces nativas, oferecendo um equilíbrio ideal entre eficiência e personalização.
Em vez de tentar abstrair completamente a plataforma nativa, o KMP empodera devs nativos com um maquinário open-source que trata de compilar as aplicações para Android, iOS, Web, macOS, Windows, Linux entre outros.
O KMP visa:
- Manter o desenvolvimento de recursos específicos da plataforma tão natural e próximo quanto possível do desenvolvimento nativo.
- Assegurar que os desenvolvedores nativos não enfrentem dificuldades ao trabalhar com o código compartilhado.
- Facilitar a interoperabilidade entre o código nativo e compartilhado, tornando a interação com o código compartilhado intuitiva e familiar.
Flexibilidade e UI Nativa
Com KMP, as versões do seu aplicativo podem ter muito em comum, mas também diferir significativamente, especialmente em termos de UIs.
KMP não impõe limitações em como você desenvolve a UI do seu aplicativo. Você pode usar qualquer estilo e frameworks que quiser, incluindo os mais modernos, como Jetpack Compose para Android e SwiftUI para iOS. Isso permite o uso de elementos específicos de cada plataforma, proporcionando uma experiência de UI nativa para seus usuários.
Compartilhando código Kotlin com as plataformas
Dado o espírito flexível do KMP, atualmente existem uma série de estratégias que devs podem adotar para usar o KMP.
Algumas abordagens comuns:
- Compartilhando modelos do domínio: Utilização de classes comuns como entidades, DTOs (Data Transfer Objects), respostas do servidor, etc., consistentes em todas as plataformas.
- Componentes de infraestrutura: Compartilhamento de lógica relacionada ao uso da internet, persistência de dados, e manipulação de cache, etc.
- Experimentação e analytics: Códigos que permite experimentação no app, como a definição de feature flags, eventos de analytics, etc.
- Lógica de negócios: Códigos que definem regras de negócios, validações, e algoritmos essenciais para o funcionamento da aplicação.
- Utilitários: Funções e classes auxiliares que podem ser usadas em diferentes partes da aplicação, como manipulação de strings, formatação de datas, constantes, etc.
- Testes: Escrever testes unitários e de integração que podem ser executados em todas as plataformas, garantindo a consistência e a confiabilidade do código compartilhado.
- Abstrações de Hardware e SO: Códigos que abstraem funcionalidades específicas do sistema operacional ou do hardware, como acesso a sensores, armazenamento de arquivos, etc., permitindo que sejam usados de maneira uniforme em diferentes plataformas.
Lembrando que a escolha de quais partes compartilhar depende das necessidades específicas do projeto e da equipe. O KMP oferece a flexibilidade para adaptar a estratégia de compartilhamento de código conforme o projeto evolui.
Considerações finais
Nesse artigo, conseguimos sair do zero no mundo KMP e compreendemos tecnicamente a diferença entre desenvolvimento nativo, cross-plataforma e multiplataforma.
Em resumo, cada tecnologia - React Native, Flutter e Kotlin Multiplatform - tem seus pontos fortes e fracos.
Ao escolher a ferramenta certa para o seu projeto, considere fatores como desempenho, facilidade de uso e suporte da comunidade. Kotlin Multiplatform emerge como uma opção promissora, especialmente para quem valoriza a eficiência do código compartilhado sem comprometer a experiência do usuário nativo.
Com esse conhecimento, podemos avançar para os conceitos mais específicos do funcionamento do Kotlin Multiplataforma, como o compilador, síntaxe, configuração, etc.
Próximos passos
Iremos aprender como o compilador do Kotlin funciona, e como sua estrutura de frontend + backend + IR possibilitam as múltiplas compilações.
🤖 Artigo foi escrito com o auxílio do ChatGPT 4, utilizando o plugin Web.
As fontes e o conteúdo são revisados para garantir a relevância das informações fornecidas, assim como as fontes utilizadas em cada prompt.
No entanto, caso encontre alguma informação incorreta ou acredite que algum crédito está faltando, por favor, entre em contato!
Referencias
Top comments (1)
Parabéns pelo conteúdo, acredito que é um dos materias mais completos que já li sobre tecnologias multiplataforma!