DEV Community

Rodrigo Sicarelli
Rodrigo Sicarelli

Posted on • Updated on

KMP 101: Introdução ao paradigma da Multiplataforma

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.

🔗 Versão interativa

Desenvolvimento nativo

🔗 Decision framework for mobile development

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].

Flutter Play Store 2021

  • 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].

Flutter Play Store 2022

  • 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].

Flutter Play Store 2023

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 um UITextView no iOS.
  • No Android, o mesmo componente Text se torna um TextView.

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:

  1. Thread do JavaScript: Responsável pela execução do código JavaScript.
  2. Main Thread Nativa: Ou "thread principal", gerencia a UI e as interações do usuário.
  3. 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.

🔗 Versão interativa

Arquitetura estável do React Native

🔗 Understanding React Native Architecture

Desafios da Arquitetura Antiga

  1. Assincronicidade: a ponte opera de forma assíncrona, causando possíveis atrasos na atualização da UI quando a espera não é necessária.
  2. Execução Single-threaded do JavaScript: restringe todas as computações a uma única thread, podendo causar bloqueios e atrasos em operações intensivas.
  3. 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.

🔗 Versão interativa

React Native nova arquitetura

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

🔗 A guide to Turbo Modules in React Native

🔗 Documentação oficial sobre a nova arquitetura


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:

🔗 Versão interativa

Flutter SDK

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.

Nativo vs Flutter vs Compose

🔗 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.

🔗 Versão interativa

KMP compartilhando código

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.

🔗 Gráfico em tela cheia

Arquitetura simplificada KMP

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.

🔗 Versão interativa

Arquitetura KMP

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)

Collapse
 
drawiin profile image
Vinicius Guimarães

Parabéns pelo conteúdo, acredito que é um dos materias mais completos que já li sobre tecnologias multiplataforma!