bazel mobile-install

Informar um problema Mostrar fonte Por noite · 7,3 · 7,2 · 7,1 · 7,0 · 6,5

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:

  1. Encontre a regra android_binary do app que você quer instalar.
  2. Desative o Proguard removendo o atributo proguard_specs.
  3. Defina o atributo multidex como native.
  4. Defina o atributo dex_shards como 10.
  5. Conecte seu dispositivo com ART (não Dalvik) via USB e ative o USB depurando.
  6. Execute bazel mobile-install :your_target. A inicialização do app vai ser um pouco mais lento do que o normal.
  7. Edite o código ou os recursos do Android.
  8. Execute bazel mobile-install --incremental :your_target.
  9. 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 de adb. 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-installfaz 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:

  1. Como instalar o arquivo .apk (geralmente usando adb install)
  2. 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 classe Application em ContentProvider#onCreate() Esse método é chamado durante a fase de teste inicialização antes de substituirmos a instância do Application 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 de Context 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 usando PackageManager#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)