Sobre os recursos da arquitetura Android através dos olhos de um desenvolvedor não Android

Recentemente, redesenhamos completamente o aplicativo Pyrus para Android. A primeira versão do aplicativo já funcionava no Android 2.2. Recusando-se a oferecer suporte ao Android abaixo de 4.1, conseguimos pagar a dívida técnica acumulada e simplificamos significativamente o código-fonte. Sim, perdemos uma parte dos usuários (menos de 1%), mas, por outro lado, economizamos tempo para os desenvolvedores corrigirem erros raros. Poderemos investir no desenvolvimento de funcionalidades para todos os usuários atuais e novos. A longo prazo, isso é muito mais importante.

Aqui, compartilhamos experiências que podem ser úteis para aqueles que estão pensando em iniciar o desenvolvimento da plataforma Android.

O Android atrai boas ferramentas de desenvolvimento, linguagem testada pelo tempo (Java) com a sintaxe usual, uma enorme audiência de usuários. No entanto, é difícil criar aplicativos convenientes no Android: apesar da API desenvolvida, muitas vezes acontece que não há componentes prontos com comportamento adequado. Você precisa abandonar as idéias do designer de UX ou concordar em depender de bibliotecas de terceiros ou escrever a si mesmo. Nem todas as soluções arquiteturais do Android foram bem-sucedidas, e isso também aumenta a quantidade de código sem benefícios óbvios.

Morrer e ressuscitar das cinzas


A interface no Android é baseada na atividade. Este é um contêiner que normalmente ocupa a tela inteira do dispositivo, outros widgets vivem nele e recebe eventos sobre a interação do usuário.

Quando o usuário ativa o aplicativo, um evento onResume é lançado na Atividade. Pressionou o botão Início - a atividade desapareceu, mas antes disso ele recebeu o evento onPause. Até agora, tudo é lógico. O que acontece quando um usuário gira a tela do dispositivo 90 graus? Você nunca adivinhará: o SO mata Atividade e recria! Quando você desinstala uma Atividade, todos os componentes que ela contém são excluídos. Isso significa que você precisa escrever um código especial que salve o estado dos widgets dentro da Atividade (posição de rolagem, parte do texto selecionada, estado das caixas de seleção) e os restaure depois que o pai for recriado. O Android faz parte desse trabalho para você, mas não para todos.

É difícil entender o que levou os arquitetos a tomar essa decisão. Mais tarde, alguém decidiu que parecia feio e adicionou o mesmo parâmetro, que suprime o comportamento padrão do sistema. Agora, em vez de recriar Activity, o sistema operacional pode chamar o método onConfigurationChanged especial. Essa "solução" parece um hack sujo e apenas agrava o problema, porque a documentação do Android diz que "essa técnica deve ser considerada uma medida extrema e não é recomendada para a maioria dos aplicativos".

Fragmentos fragmentados


Inicialmente, o sistema operacional Android foi criado para telefones. Com o advento dos tablets, a equipe do Android decidiu com razão que os elementos da interface que antes eram espaçados por diferentes atividades devido ao pequeno tamanho da tela do telefone agora podem coexistir na tela grande do tablet. (Exemplo: a lista de cartas e o e-mail no telefone estavam em duas atividades diferentes e no tablet eles compartilhavam uma tela juntos.) Sim, o problema é: várias atividades não podem interagir simultaneamente com o usuário por design. Portanto, é necessário outro mecanismo para reutilizar componentes e foi encontrada uma solução: fragmentos apareceram (Fragmento).

Por definição , um Fragmento representa um comportamento ou parte de uma interface do usuário em uma Atividade. Tudo está claro. No entanto, nas instruções de uso de fragmentos, encontramos a seção “Adicionando um fragmento que não possui uma interface de usuário”. O que? Acontece que um fragmento pode não ter sua própria janela para renderização!

Mas o que acontece com os fragmentos ao virar a tela? Os desenvolvedores do Android pensaram sobre isso: quando você recria uma atividade, todos os seus fragmentos também são recriados automaticamente. No entanto, após uma análise cuidadosa, o fragmento possui o método "setRetainInstance"; se você o chamar, esse fragmento não será excluído e restaurado durante as curvas.

Delgado e consistente, esse conceito não pode ser chamado.

Operação assíncrona


O encadeamento de processamento de eventos não pode ser bloqueado por operações longas, portanto, a troca de dados com o disco e a rede deve ser realizada de forma assíncrona em outros encadeamentos. O Android fornece até quatro mecanismos para operação assíncrona: AsyncTask, Service, IntentService, Thread. Os desenvolvedores são incentivados a descobrir qual é o melhor para eles usarem. A escolha não é trivial: por exemplo, se o aplicativo iniciou o AsyncTask e o usuário girou a tela durante sua execução, após a conclusão do trabalho, o AsyncTask não conseguiu encontrar uma nova atividade (criada após a mudança). E nenhum dos quatro mecanismos implementa lógica simples: se o usuário iniciou o segundo processo assíncrono sem aguardar os resultados do primeiro e, em seguida, o primeiro ainda terminou anteriormente, é apropriado ignorar e não refletir seus resultados na interface do usuário.

Torna-se clara a aparência de várias bibliotecas - barramentos de dados para organizar o trabalho assíncrono dentro de um aplicativo Android.

Reiniciando o aplicativo após o OOM


Se o Android ficar sem memória RAM, o sistema operacional poderá concluir qualquer processo. Quando o usuário ativar o aplicativo no futuro, o sistema tentará restaurar seu estado. Em teoria, isso deve ser invisível para o desenvolvedor (se ele implementou o método onSaveInstanceState em todos os componentes): o próprio sistema recria a pilha de atividades como era durante a parada forçada. No entanto, se a inicialização demorar algum tempo (por exemplo, carregar caches do disco ou da rede), será correto mostrar ao usuário um indicador de espera. Acontece que, ao criar uma Atividade, você ainda precisa monitorar o “cold start” e executar a inicialização manualmente. Sem mencionar que, ao reiniciar, nenhum Thread e AsyncTask são restaurados.

Qual a probabilidade de ficar sem memória na prática? Normalmente, essa situação ocorre ao processar imagens de alta resolução. Por exemplo, uma imagem de 2048x1536 ocupa 12Mb na memória de um objeto Bitmap. A quantidade de memória disponível para o aplicativo depende do modelo específico do dispositivo e, às vezes, é muito pequena (64 Mb ou 128 Mb). Como o coletor de lixo em todas as versões do Android anteriores à 8.0 não era compacto, mesmo se você tivesse 100Mb de memória livre, mas estivesse dividido em blocos de 10Mb, uma tentativa de alocar memória para uma imagem desse tipo causou uma falha no aplicativo.

Coleta de lixo eterno


Uma vez que nosso usuário percebeu que, ao percorrer as listas longas, o aplicativo ficava atrasado - a cada 2-3 segundos, a animação parava por breves pausas (200-300 ms) e continuava. A análise mostrou que a coleta de lixo geralmente é iniciada com desconfiança no aplicativo. Descobriu-se que a classe HashMap padrão (que usamos para obter um objeto Java por seu ID) é ineficiente no nosso caso: precisamos criar um objeto wrapper para cada chave, que é um número inteiro (int). Portanto, o número de alocações de memória aumenta sem nenhum benefício. A solução foi mudar para um contêiner SparseArray especial (disponível apenas no Android, não na plataforma Java padrão), o que reduziu imediatamente a pressão no coletor de lixo.

O que o atraso na animação tem a ver com isso, você pergunta? O fato é que o coletor de lixo no Android interrompeu todos os threads durante sua operação, incluindo o principal envolvido na renderização. Nas versões do Android a partir do 5.0, outra máquina virtual é usada (ART em vez de Dalvik) e um algoritmo de coleta de lixo diferente, que interrompe a animação cada vez menos. (Você pode ler sobre a comparação do tempo de coleta de lixo aqui .)

Exibir documentos


Se você deseja inserir visualizações de documentos em seu aplicativo, é forçado a decepcionar: no Android, não há componente interno que possa exibir arquivos do Word, Excel, Powerpoint. Sem mencionar arquivos ZIP ou documentos PDF. Saída? Force o usuário a instalar aplicativos de terceiros para exibir cada tipo de arquivo ou usar o componente WebView (na verdade, um navegador). Nos dois casos, quando o usuário grava o arquivo, um aplicativo externo será iniciado e alguns cenários simplesmente não podem ser implementados, por exemplo, um feed de imagem: não há uma maneira fácil de integrar o WebView a um ViewPager padrão.

O que vem a seguir


Alguns estudos afirmam que a complexidade do desenvolvimento do Android é relativamente alta . Nossa experiência mostra que é fácil para um desenvolvedor competente se acostumar com os recursos desta plataforma. E embora hoje o Android seja o sistema operacional móvel mais popular do mundo , em 5 a 10 anos a situação pode mudar radicalmente.

Nos últimos anos, o Google desenvolveu secretamente o novo OC Fuchsia com um modelo de segurança fundamentalmente diferente (comparado ao sistema operacional moderno). Provavelmente, usará o Dart como a principal linguagem de programação e a estrutura Flutter como a principal maneira de criar aplicativos. Há rumores de que o Fuchsia pode substituir o Android e o ChromeOS em todos os dispositivos. Se o Google fizer isso, é provável que a empresa forneça suporte nativo para aplicativos criados para Android (como a Microsoft fez ao mudar do DOS para o Windows). Portanto, até agora você não pode se preocupar e continuar acumulando experiência no Android. E para aqueles que querem olhar para o futuro, você pode baixar o Flutter e brincar com ele aqui .

Source: https://habr.com/ru/post/pt416689/


All Articles