Desenvolvimento iterativo rápido para Android
Nesta página, descrevemos como o bazel mobile-install
facilita o desenvolvimento iterativo.
para Android muito mais rápido. Descreve os benefícios dessa abordagem em relação
e os desafios do método
tradicional de instalação de apps.
Resumo
Para instalar pequenas mudanças em um app Android muito rapidamente, faça o seguinte:
- Encontre a regra
android_binary
do app que você quer instalar. - Desative o Proguard removendo o atributo
proguard_specs
. - Defina o atributo
multidex
comonative
. - Defina o atributo
dex_shards
como10
. - Conecte seu dispositivo com ART (não Dalvik) via USB e ative o USB depurando.
- Execute
bazel mobile-install :your_target
. A inicialização do app vai ser um pouco mais lento do que o normal. - Edite o código ou os recursos do Android.
- Execute
bazel mobile-install --incremental :your_target
. - Gosta de não ter que esperar muito.
Algumas opções de linha de comando para o Bazel que podem ser úteis:
--adb
informa ao Bazel qual binário adb usar.--adb_arg
pode ser usado para adicionar outros argumentos à linha de comando deadb
. Uma aplicação útil é selecionar o dispositivo que deseja instalar se houver vários dispositivos conectados à estação de trabalho:bazel mobile-install --adb_arg=-s --adb_arg=<SERIAL> :your_target
--start_app
inicia o app automaticamente
Na dúvida, consulte exemplo ou entre em contato.
Introdução
Um dos atributos mais importantes do conjunto de ferramentas de um desenvolvedor é a velocidade: há muita diferença entre alterar o código e vê-lo ser executado em um e ter que esperar minutos, às vezes horas, antes de receber qualquer feedback para verificar se as mudanças fazem o que você espera que façam.
Infelizmente, a cadeia de ferramentas tradicional do Android para criar um arquivo .apk exige muitas etapas sequenciais e monolíticas, e todas elas precisam ser feitas para criar um app Android. No Google, esperar cinco minutos para criar uma linha única não era incomum em projetos maiores, como o Google Maps.
bazel mobile-install
agiliza muito o desenvolvimento iterativo do Android ao
usando uma combinação de remoção de mudanças, fragmentação de trabalho e manipulação inteligente de
Por dentro do Android, tudo sem alterar qualquer código do seu app.
Problemas com a instalação tradicional de apps
A criação de apps Android apresenta alguns problemas, incluindo:
Dexação. Por padrão, "dx" é invocado exatamente uma vez na compilação e não saber como reutilizar o trabalho de builds anteriores: ele dexa todos os métodos novamente, mesmo embora apenas um método tivesse sido alterado.
Fazendo upload dos dados para o dispositivo. O adb não usa toda a largura de banda de um USB 2.0 e o upload de apps maiores pode levar mais tempo. Todo o app é mesmo que apenas partes pequenas tenham sido alteradas, por exemplo, um recurso ou método único, o que pode ser um grande gargalo.
Compilação em código nativo. O Android L introduziu o ART, um novo tempo de execução do Android, que compila aplicativos com antecedência em vez de compilá-los no momento certo, como Dalvik Isso torna os apps muito mais rápidos, mas com uma instalação mais longa. tempo de resposta. Essa é uma boa vantagem para os usuários, porque eles normalmente instalam um app uma vez e usá-lo muitas vezes, mas isso resulta em um desenvolvimento mais lento em que o aplicativo é instalada várias vezes e cada versão é executada no máximo algumas vezes.
A abordagem de bazel mobile-install
bazel mobile-install
faz as seguintes melhorias:
Dexação fragmentada. Depois de criar o código Java do app, o Bazel fragmenta a classe em partes aproximadamente do mesmo tamanho e invoca
dx
separadamente no para resolvê-los com rapidez.dx
não é invocado em fragmentos que não mudaram desde o último build.Transferência de arquivos incremental. Recursos do Android, arquivos .dex e arquivos são removidas do .apk principal e armazenadas em um mobile-install. Com isso, é possível atualizar o código e recursos de maneira independente, sem reinstalar o app inteiro. Assim, transferir os arquivos leva menos tempo e somente os arquivos .dex que têm alterados são recompilados no dispositivo.
Carregar partes do app de fora do .apk. Um pequeno aplicativo de stubs colocar no .apk que carrega recursos Android, código Java e código nativo do diretório de instalação móvel no dispositivo e, em seguida, transfere o controle para o aplicativo real. Tudo isso é transparente para o app, exceto em alguns casos extremos descritas abaixo.
Dexação fragmentada
A dexação fragmentada é bem simples: depois que os arquivos .jar são criados, uma
ferramenta
os fragmenta em arquivos .jar separados de tamanhos aproximados e, em seguida, invoca
dx
nos que foram alterados desde o build anterior. A lógica que
determina quais fragmentos dexar não é específico do Android: ele apenas usa o
algoritmo de poda de mudanças gerais do Bazel.
A primeira versão do algoritmo de fragmentação simplesmente ordenou os arquivos .class em ordem alfabética e depois recortou a lista em partes do mesmo tamanho, mas isso provou ser abaixo do ideal: se uma classe foi adicionada ou removida (mesmo uma classe aninhada primeiro), todas as classes em ordem alfabética depois seriam deslocadas em uma unidade, resultando na dexação desses fragmentos novamente. Por isso, foi decidido fragmentar o Java pacotes em vez de classes individuais. Claro, isso ainda resulta dexação de muitos fragmentos se um novo pacote for adicionado ou removido, mas isso é muito menos mais frequentes do que adicionar ou remover uma única classe.
O número de fragmentos é controlado pelo arquivo BUILD (usando o
android_binary.dex_shards
). Em um mundo ideal, Bazel
determina automaticamente quantos fragmentos são melhores, mas o Bazel atualmente precisa saber
o conjunto de ações (por exemplo, comandos a serem executados durante a compilação) antes
executar qualquer um deles, portanto não pode determinar o número ideal de fragmentos
porque não sabe quantas classes Java existirão no
app. De modo geral, quanto mais fragmentos, mais rápida é a compilação
será instalado, mas a inicialização do app mais lenta se tornará, porque a dinâmica
o vinculador precisa trabalhar mais. O ponto ideal geralmente fica entre 10 e 50 fragmentos.
Transferência de arquivos incremental
Depois de criar o aplicativo, a próxima etapa é instalá-lo, de preferência com o o mínimo de esforço possível. A instalação consiste nas etapas a seguir:
- Como instalar o arquivo .apk (geralmente usando
adb install
) - Fazer upload dos arquivos .dex, recursos do Android e bibliotecas nativas para Diretório de instalação de dispositivos móveis
Não há muita incrementabilidade na primeira etapa: o app está instalado
ou não. No momento, o Bazel depende do usuário para indicar se precisa concluir essa etapa.
pela opção de linha de comando --incremental
porque não consegue determinar
em todos os casos, se necessário.
Na segunda etapa, os arquivos do app do build são comparados a um arquivo no dispositivo de manifesto que lista os arquivos de apps que estão no dispositivo e os checksums. Todos os novos arquivos são enviados para o dispositivo e os arquivos que foram alterados serão atualizados e quaisquer arquivos que tenham sido removidos serão excluídos da dispositivo. Se o manifesto não estiver presente, presume-se que todos os arquivos precisam ser enviados.
É possível enganar o algoritmo de instalação incremental alterar um arquivo no dispositivo, mas não a soma de verificação no manifesto. Isso poderia foram protegidos com o cálculo da soma de verificação dos arquivos no dispositivo, mas foi considerado que isso não compensa o aumento no tempo de instalação.
O aplicativo Stub
O aplicativo stub é onde a mágica para carregar os dexes, o código nativo e
Os recursos do Android do diretório mobile-install
no dispositivo são executados.
O carregamento real é implementado pela subclasse BaseDexClassLoader
e é uma
uma técnica razoavelmente bem documentada. Isso acontece antes de qualquer
classes sejam carregadas, de modo que todas as classes de aplicativo que estejam no apk possam ser
colocados no diretório mobile-install
no dispositivo para que possam ser atualizados.
sem adb install
.
Isso precisa acontecer antes que qualquer classes do aplicativo sejam carregadas, de modo que nenhuma classe de aplicativo precise estar no .apk, o que significaria que as mudanças nessas classes exigiriam um arquivo e reinstale-o.
Isso é feito substituindo a classe Application
especificada no
AndroidManifest.xml
com o
aplicativo de stub. Isso
assume o controle quando o aplicativo é iniciado e ajusta o carregador de classes e o
Resource Manager adequadamente no início (seu construtor) usando
Reflexão de Java sobre os aspectos internos do framework do Android.
Outra coisa que o aplicativo com stub faz é copiar as bibliotecas nativas
instalado por dispositivos móveis
em outro local. Isso é necessário porque
o vinculador dinâmico precisa que o bit X
seja definido nos arquivos, o que não é possível para
para qualquer local acessível por uma adb
não raiz.
Depois que tudo isso é feito, o aplicativo de stub instancia
a classe Application
real, mudando todas as referências a si mesma para a classe
no framework do Android.
Resultados
Desempenho
Em geral, bazel mobile-install
resulta na aceleração de 4 a 10 vezes na criação
e instalar apps grandes após uma pequena mudança.
Os números a seguir foram calculados para alguns produtos do Google:
Obviamente, isso depende da natureza da mudança: a recompilação após mudar uma biblioteca de base leva mais tempo.
Limitações
Os truques do aplicativo de stub não funcionam em todos os casos. Os casos a seguir destacam onde ele não funciona como esperado:
Quando
Context
é transmitido para a classeApplication
emContentProvider#onCreate()
Esse método é chamado durante a fase de teste inicialização antes de substituirmos a instância doApplication
Portanto,ContentProvider
ainda fará referência ao aplicativo de stub em vez do verdadeiro. Pode-se argumentar que isso não é um bug, já que você não está deve ter diminuído o nível deContext
assim, mas isso parece acontecer em alguns no Google.Os recursos instalados por
bazel mobile-install
estão disponíveis apenas no o app. Se os recursos forem acessados por outros apps usandoPackageManager#getApplicationResources()
, estes recursos serão da última instalação não incremental.Dispositivos que não executam o ART. O aplicativo de stub funciona bem Froyo e, mais tarde, o Dalvik tem um bug que faz pensar que o app incorreto se seu código for distribuído por vários arquivos .dex em determinados casos em que as anotações Java são usadas em um específicas de alguma maneira. Contanto que seu app não mostre esses bugs, ele deve funcionar com o Dalvik, Observação: o suporte para versões antigas do Android não é exatamente nosso foco)