Compactando o APK, tentando mantê-lo funcionando


/ PxHere / PD


Otimizar o peso do APK é uma tarefa não trivial, mas muito relevante nos dias do Instant App. A ativação do programa salva você de código desnecessário se suas dependências puderem ser determinadas no estágio de compilação, mas existem vários outros tipos de arquivos no APK que podem ser excluídos do assembly.


Sob o código de como criar dependências - definidas no estágio de compilação, quais arquivos podem ser excluídos do assembly e como fazê-lo, além de analisarmos como excluir componentes não utilizados do assembly, se você tiver vários aplicativos com uma base de código comum.


Antes de ler


  • Antes de aplicar as dicas do artigo, otimize o APK para o guia do Google . Este artigo é para aqueles que não possuem otimizações padrão suficientes.
  • Por "proguard", quero dizer um compilador de otimização com minificação.
  • Por componente, quero dizer uma certa característica do produto do ponto de vista comercial. No nosso caso, isso é apenas uma coleção de arquivos em um determinado pacote. Temos um módulo gradle para toda a aplicação.

O peso do nosso APK otimizado para o Google era de 4.4 .


Arquivos extras


Vamos começar com um simples. Se você não usar o kotlin-reflect , poderá excluir da montagem as meta-informações sobre as classes do kotlin. Você pode fazer isso da seguinte maneira:
No build.gradle (Module: app)


 android { packagingOptions { exclude("META-INF/*.kotlin_module") exclude("**.kotlin_builtins") exclude("**.kotlin_metadata") } } 

A reflexão Java não precisa dos *.kotlin_module , *.kotlin_builtins e *.kotlin_metadata . Determinar qual reflexão você está usando é muito simples. Se você escrever obj::class.<method> , use reflexão kotlin, se obj::class.java.<method> , em seguida, reflexão java.


O resultado da otimização para nós: -602,1 kb


Dependências


Às vezes, as bibliotecas adicionam dependências para casos que nunca acontecem no seu aplicativo. Por exemplo, o ktor-client puxa o kotlin-reflect junto com ele (0,5 mb!).
minifyEnabled = true com esses casos da seguinte maneira: coletei o APK com minifyEnabled = true , joguei-o no analisador Android Studio, baixei o mapping.txt e procurei pacotes que, em teoria, não deveriam estar presentes na montagem. Por exemplo, kotlin.reflect . Após executar ./gradlew app:dependencies na pasta do projeto para procurar dependências (não esqueça de aumentar o tamanho do histórico no terminal. A árvore de dependências pode ser grande!). Nesta árvore, é fácil entender o que se refere a dependências desnecessárias e excluí-las. No build.gradle seu módulo:


 dependencies { implementation("io.ktor:ktor-client-core:$ktorVersion") { exclude(group: "org.jetbrains.kotlin", module: "kotlin-reflect") } implementation("io.ktor:ktor-client-okhttp:$ktorVersion") { exclude(group: "org.jetbrains.kotlin", module: "kotlin-reflect") } } 

Esse código remove a dependência da biblioteca ktor-client no kotlin-reflect . Se você deseja excluir outra coisa, substitua seus valores.


!!! Use este conselho com muito cuidado! Antes de eliminar dependências, verifique se você não precisa delas. Caso contrário, o aplicativo poderá começar a cair na produção !!!


O resultado da otimização para nós: -500,3 kb


Valide seu XML


Infelizmente, o programa não remove outros arquivos de marcação XML da pasta de layout. XML não utilizado pode usar widgets "pesados" e o programa não poderá excluí-los do assembly também! Para evitar isso, remova recursos não utilizados com Refactor -> Remove unused resources...


Verifique seus di


Se você, como nós, usa DI de tempo de execução, verifique se possui provedores para essas dependências que não está usando. O programa não pode excluí-los do assembly porque eles não são usados ​​do ponto de vista do compilador. Você os utiliza ao criar um gráfico de dependência.


Excluir dependências de depuração das versões da versão


As ferramentas de depuração podem ocupar muito espaço inesperadamente. Por exemplo, stetho pesa cerca de 0.2 após a compactação! De qualquer forma, é melhor excluir toda a infraestrutura de depuração da compilação do release, para que ninguém possa aprender muito sobre seu aplicativo simplesmente baixando-o do Google Play.


Você pode criar versões diferentes dos mesmos arquivos para depuração e lançamento. Para fazer isso, na pasta src , ao lado de main , crie as pastas de debug e release . Agora você pode gravar a função initStetho que inicializa o Stetho no arquivo src/debug/java/your/pkg/Stetho.kt e a função initStetho que não faz nada no src/debug/java/your/pkg/Stetho.kt src/release/java/your/pkg/Stetho.kt .


Por precaução, verifique se essa dependência está incluída apenas nas compilações de depuração. Você pode fazer isso substituindo a implementation por debugImplementation em build.gradle . Na maioria das vezes, o programa elimina arquivos desnecessários, mesmo sem essa etapa, mas nem sempre. A resposta para a pergunta "por quê?" abaixo no texto do artigo .


Plataformas


Às vezes, na mesma base de código, várias versões diferentes de um aplicativo são emitidas. Podem ser versões diferentes para diferentes países ou regiões ou, como no nosso caso, para diferentes clientes. Abaixo estão algumas dicas sobre como descarregar a plataforma.



/ PxHere / PD


Nossa experiência


Estamos desenvolvendo um designer de aplicativos móveis E-SHOP . Temos várias dezenas de clientes e cada um tem seu próprio conjunto individual de componentes. Alguns componentes são usados ​​por todos os clientes, outros são apenas parte. Nossa tarefa é incluir na montagem do cliente apenas os componentes que ele precisa.


Exceção de sinalizador


Para cada cliente, criamos um productFlavor separado. Isso é conveniente porque é fácil criar recursos diferentes para clientes diferentes, o IDE fornece uma interface gráfica para alternar entre sabores e os caches funcionam bem. E você também pode gerar seu próprio BuildConfig.java para cada cliente. Os valores de campo dessa classe são conhecidos no estágio de compilação. É disso que precisamos! Crie um campo do tipo boolean para cada componente.


 android { productFlavors { client1 { buildConfigField("boolean", "IS_CATALOG_ENABLED", "true") } client2 { buildConfigField("boolean", "IS_CATALOG_ENABLED", "false") } } } 

Esta é uma versão simplificada da configuração. O presente é complexo devido à integração com o nosso IC.


Agora é sabido se o componente está ativo no estágio de compilação e o programa pode excluí-lo da montagem!


XML novamente


Agora, o problema com layouts XML não utilizados assume uma nova dimensão! Você não pode simplesmente tirar e remover a marcação de um componente simplesmente porque alguns clientes não precisam.


Em nossa aplicação XML de um dos componentes raramente usados, usamos um widget que se refere à biblioteca de reconhecimento de imagens firebase.ml.vision . Ele pesa cerca de 0,2 mb, o que é muito. Decidiu-se adicionar este widget com código em vez de declará-lo na marcação. Depois disso, a proguard conseguiu excluir a vision da assembléia para clientes que não precisam dela.


O resultado da otimização para nós: -222,3 kb para o APK médio


@Keep


Existem duas maneiras de informar ao proguard que sua classe não pode ser reduzida: escreva uma regra no arquivo proguard-rules.pro ou coloque a anotação @Keep . Na biblioteca play-services-vision , esta anotação está na classe raiz. Portanto, 0,2 MB ficaram paralisados ​​mesmo nos aplicativos clientes que não precisam de reconhecimento de imagem.


Não encontrei uma maneira simples e segura de remover esta anotação. Se você souber como - por favor escreva nos comentários.


Felizmente, a biblioteca firebase.ml.vision , que é uma versão mais recente do play-services-vision , não usa essa anotação e resolvemos o problema.


E novamente DI


Por último mas não menos importante. DI para componentes desconectados. Tudo é simples aqui: para cada componente, usamos nosso próprio contêiner e conectamos as dependências gerais através de um módulo separado.


O resultado da otimização para nós: -20,1 kb para o APK médio


Conclusões


  • O peso do APK médio diminuiu de 4.4 para 3.1 e o mínimo - para 2.5 !
  • O código do aplicativo não foi prejudicado, mas melhorado. Agora é mais fácil trabalhar com DI.

Todas as otimizações apresentadas no artigo são "frutos baixos". Eles são muito fáceis de implementar e obtêm rapidamente o resultado. Até -43% para um APK já otimizado no nosso caso. Espero ter economizado seu tempo listando tudo em um só lugar.


Obrigado a todos!

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


All Articles