
Olá, sou desenvolvedor Android e não tenho mais medo do ProGuard ...
Geralmente, esse utilitário é lembrado quando se depara com um problema do dalvik dex-limit ou com um requisito para melhorar a segurança do aplicativo. Infelizmente, está longe da primeira vez para configurar corretamente o Proguard. Frequentemente, observei quantas, ao interromper o projeto, desligam o Proguard e ativam o suporte ao Mulditex, e toda vez que fico triste com isso, porque o Proguard ajuda a reduzir o tamanho do aplicativo e aumentar seu desempenho.
No final, decidi escrever um artigo no qual eu pudesse colocar todas as informações úteis que aprendi ao longo de vários anos trabalhando com a Proguard e que poderiam ajudar tanto novatos quanto aqueles que já sabem alguma coisa.
Sobre o que é isso
Proguard é um utilitário de código aberto para otimizar e ofuscar o código Java. Essa ferramenta processa o código Java já compilado, portanto, deve funcionar com qualquer linguagem da JVM. Mais precisamente, a própria linguagem para Proguard é indiferente, apenas o bytecode é importante. Todas as manipulações de bytecode programadas podem ser divididas em 3 categorias principais: redução de código , otimização e ofuscação .
Código encolhendo
Sim, é uma tarefa bastante estranha escrever código e excluí-lo, mas essa é a realidade do desenvolvimento do Android. Obviamente, não se trata do código escrito à mão (embora isso aconteça), mas de toneladas de carga morta trazidas por bibliotecas de todos os tipos. Goiaba , Apache Commons , Google Play Services e outros caras podem aumentar o tamanho do arquivo apk de 500kb para algumas dezenas de megabytes. Às vezes, é tão longe que um programa não pode ser compilado devido a exceder o limite dos métodos Dalvik . O Proguard ajudará a remover todo o código não utilizado e reduzirá o tamanho do aplicativo para alguns megabytes.
Otimização
Além de remover o código desnecessário, o Proguard pode otimizar o código restante. Seu arsenal inclui análise de fluxo de controle, análise de fluxo de dados, avaliação parcial, atribuição única estática, numeração de valor global, análise de vivacidade. Proguard pode executar otimizações de olho mágico, reduzir o número de alocações de variáveis, simplificar recursões de cauda e muito mais ( wiki ). Além dessas operações gerais, o Proguard possui otimizações úteis especificamente para a plataforma Android, por exemplo, substituindo classes enum por ints, excluindo instruções de registro.
Ofuscação
Finalmente, o Proguard pode transformar todo o seu código em uma confusão ilegível renomeando todas as classes, métodos e campos em conjuntos de letras aleatórias (na verdade não muito aleatórias). Essa é uma opção muito útil, pois qualquer pessoa pode descompilar seu arquivo apk e nem todos têm paciência para entender o código ofuscado.
Princípio de funcionamento
O programa funciona em 3 etapas na sequência descrita acima: encolhimento do código → otimização → ofuscação . Cada uma das etapas é opcional.
A etapa de otimização no caso do Android SDK está desativada por padrão.
Para o Proguard funcionar, você precisa fornecer 3 componentes:
- Seu código compilado é um arquivo com os arquivos de
class
do seu programa e todas as bibliotecas que você usa (jar, aar, apk, war, zip, etc.). Proguard apenas modifica o código já compilado e não tem nada a ver com a fonte. - Arquivo (s) de configuração - arquivo (s) que contém todas as regras, opções e configurações com as quais você deseja iniciar o processamento.
- Jarros da biblioteca (aar, apks, ...) - classes da plataforma na qual o programa é executado. No caso do Android, este é
android.jar
. Esses arquivos são necessários apenas para a análise correta do seu código, eles não serão modificados (isso não faz sentido, pois o android.jar
está "no telefone", não temos acesso a ele).

(Imagem da apresentação de Jeb Ware, link no final do artigo)
Usando classes de biblioteca e seus arquivos de configuração, o Proguard define todos os pontos de entrada para o seu programa ( sementes ). Em outras palavras, define as classes e métodos que podem ser chamados de fora e que não podem ser tocados. Então, começando com as sementes descobertas, o Proguard percorre recursivamente todo o seu código, sinalizando "usado" tudo o que poderia alcançar. Todos os outros códigos serão excluídos. Você deve especificar pelo menos um ponto de entrada na configuração. Para um programa java padrão, esta é a função main
. No Android, não há um ponto de entrada único no programa; em vez disso, temos componentes padrão (Atividade, Serviço etc.) criados e chamados pelo sistema. Felizmente, não precisamos especificar nada aqui por conta própria, o SDK do Android criará a configuração necessária para nós.
Arquivos complementares
Após detectar todos os pontos de entrada, o Proguard os seeds.txt
arquivo seeds.txt
.
Todo o código que a Proguard considerou desnecessário é gravado no arquivo usage.txt
. Este é um nome bastante estranho para um arquivo que contém código remoto; seria mais correto chamá-lo de unusage.txt, mas temos o que temos, lembre-se disso.
Na etapa de ofuscação, será criado um arquivo mapping.txt
contendo os pares <nome da classe original | método | campo> -> <nome da classe ofuscada | método | campo>. Esse arquivo é útil quando você precisa desobstruir um programa, por exemplo, ler o stacktrace. O mapeamento manual de arquivos de volta não é necessário; o SDK do Android tem utilitários de proguardui
e proguardui
para ajudar. Além disso, se você usa o Fabric Crashlytics, o plug-in gradle pode localizar e carregar esse arquivo independentemente no console, para que você não precise se preocupar com isso.
No caso do Android, esses arquivos geralmente estão localizados em app/build/output/mapping/<product-flavor-name>/
.
O Proguard também cria um arquivo dump.txt
que contém tudo o que o Proguard colocou no arquivo final. Ele nunca veio a calhar para mim, mas talvez seja útil para alguém.
Como estão as coisas no Android
O Android Gradle Plugin pode executar o Proguard por conta própria. Tudo que você precisa fazer é ativar esta opção e especificar os arquivos de configuração.
buildTypes { <...> release { minifyEnabled true proguardFiles 'proguard-rules.pro', getDefaultProguardFile('proguard-android.txt') } }
minifyEnabled true
- ativa o Proguard no tempo de construção
proguardFiles
- uma lista de arquivos de configuração. As regras de todos os arquivos de configuração serão adicionadas à lista geral na ordem em que aparecerem.
proguard-rules.pro
é o nosso arquivo de configuração com regras específicas do projeto
getDefaultProguardFile('proguard-android.txt')
- uma função que retorna o arquivo de configuração padrão para aplicativos Android. Encontra-se em AndroidSDK/tools/proguard
De fato, o SDK do Android tem dois proguard-android.txt
: proguard-android.txt
e proguard-android-optimize.txt
. O primeiro tem a opção -dontoptimize
, que desativa todas as otimizações. Se você deseja ativar a otimização, use a segunda configuração.
Além dessas configurações padrão, o SDK do Android (aapt) gera automaticamente um conjunto de regras para recursos: o aapt verifica todos os arquivos xml (incluindo o manifesto) para encontrar todas as atividades, serviços, visualizações etc. e gere as regras necessárias para eles. As regras geradas podem ser encontradas em app/build/intermediates/proguard-rules/<flavor>/aapt_rules.txt
. Você não precisa especificá-lo, o Android Gradle Plugin adicionará essas regras automaticamente.

(Imagem da apresentação de Jeb Ware, link no final do artigo)
Configs
A configuração do Proguard é a parte mais básica do trabalho e, ao mesmo tempo, a mais difícil. Uma configuração incorreta pode facilmente interromper a compilação do aplicativo e do próprio aplicativo em tempo de execução. Todas as opções de configuração disponíveis são documentadas em detalhes, desativadas. site.
Entre todas as opções, eu destacaria os três grupos mais importantes:
- manter regras - Todos os pontos de entrada possíveis para o programa. Regras que informam a Proguard quais classes ou partes de classes manter inalteradas ou quais modificações são válidas para classes específicas.
- ajuste de otimização - indique quais otimizações são aceitáveis, quantos ciclos de otimização precisam ser realizados.
- trabalhar com avisos, erros e depuração
Manter regras
Este é um conjunto de opções projetadas para proteger seu código do implacável Proguard. Na sua forma mais geral, esta regra se parece com isso:
-keep [,modifier,...] class_specification
keep
é a mais comum dessas opções (existem outras), dizendo ao Proguard para salvar a própria classe e todos os seus membros: campos e métodos.
class_specification
- um modelo apontando para a (s) classe (s) ou suas partes (membros da classe). A visão geral do modelo é muito grande, pode ser visualizada . documentação . Você pode entrar em contato com ele, mas, em geral, você pode se lembrar de que temos a oportunidade de compor um modelo desses componentes:
-keep public class com.example.MyActivity
salvar classe com.example.MyActivity
-keep public class * extends android.app.Activity
salve todas as classes públicas que herdam android.app.Activity
-keep public class * extends android.view.View { public <init>(android.content.Context); public <init>(android.content.Context, android.util.AttributeSet); public <init>(android.content.Context, android.util.AttributeSet, int); public void set*(...); }
encontre todas as classes públicas que herdam android.view.View
e salve 3 construtores com determinados parâmetros neles + todos os métodos públicos, com o modificador de void
, quaisquer argumentos e um nome começando com set
. Todas as outras partes da classe podem ser modificadas.
-keep class com.habr.** { *; }
salve todas as classes e todo o seu conteúdo no pacote com.habr
modifiers
- adição à regra de manutenção:
includedescriptorclasses
- além da classe / método / campo especificado, você precisa salvar todas as classes que ocorrem em seus descritores.includecode
- o conteúdo do método apontado por esta regra específica também não pode ser tocado.allowshrinking
- as classes apontadas por esta regra não são sementes e podem ser excluídas, mas apenas se não forem usadas no próprio programa. No entanto, se após a troca de código este código permanecer (devido ao fato de alguém o usar), é impossível otimizar / ofuscar esse código.allowoptimization
- as classes referidas por esta regra só podem ser otimizadas, mas não podem ser removidas ou ofuscadas.allowobfuscation
- as classes apontadas por esta regra só podem ser ofuscadas, mas não podem ser removidas ou otimizadas.
Além do keep
, existem mais algumas opções:
-keepclassmembers
- indica que os membros da classe devem ser salvos se a própria classe for preservada após a redução do código.
-keepclasseswithmembers
- indica que você deseja salvar classes cujo conteúdo se enquadra no modelo especificado. Por exemplo, -keepclasseswithmembers class * { public <init>(android.content.Context); }
-keepclasseswithmembers class * { public <init>(android.content.Context); }
- salva todas as classes que têm um construtor público com um argumento do tipo Context
.
-keepnames
- abreviação de -keep,allowshrinking
.
-keepclassmembernames
- abreviação de -keepclassmembers,allowshrinking
.
keepclasseswithmembernames
- abreviação de -keepclasseswithmembers,allowshrinking
.
Ajuste de otimização
A opção mais importante aqui é o sinalizador -dontoptimize
. Se estiver presente, nenhuma otimização será executada e todas as outras opções de otimização serão ignoradas.
Existem muitas opções de otimização, mas as seguintes são mais úteis para mim:
-optimizations optimization_filter
- listando todas as maneiras que você deseja usar. É melhor usar o conjunto especificado em proguard-android-optimize.txt
ou um subconjunto dele. Uma lista de todas as otimizações pode ser encontrada aqui .
-optimizationpasses n
- número de ciclos de otimização. Vários ciclos podem melhorar o resultado. Ao mesmo tempo, o Proguard é inteligente o suficiente para interromper os ciclos se perceber que o resultado não melhorou desde a última vez.
-assumenosideeffects class_specification
- indica que esse método não tem efeitos colaterais e retorna apenas algum valor. O programa irá excluir as chamadas para esse método se detectar que o resultado retornado não é usado. Talvez o uso mais comum dessa opção seja excluir todos os logs de depuração: -assumenosideeffects class android.util.Log { public static int d(...); }
-assumenosideeffects class android.util.Log { public static int d(...); }
-allowaccessmodification
- mostra tudo o que está oculto :) Uma ótima opção que permite que você se livre de -allowaccessmodification
métodos acessadores artificiais para classes aninhadas. Funciona apenas em conjunto com -repackageclasses
-repackageclasses
- -repackageclasses
mover todas as classes para um pacote especificado. Isso se aplica mais à ofuscação, mas, ao mesmo tempo, fornece bons resultados na otimização.
Outras opções úteis
-dontwarn
e -dontnote
O programa é muito inteligente e sempre relata lugares suspeitos durante a análise de código; às vezes, essas são notas, às vezes avisos. Se sua compilação não estiver funcionando com o Proguard ativado, leia todos os logs que produz, ele escreverá o que deu errado e, provavelmente, até lhe dirá como corrigi-lo. Depois de ler todas as mensagens, você corrige o problema ou ignora a mensagem de uma dessas opções se tiver certeza de que não há nenhum problema.
Por exemplo, acontece que algumas bibliotecas java usam classes de plataforma que não estão no android.jar e o Proguard avisa sobre isso. Se você tem certeza de que esta biblioteca funciona bem em um ambiente Android, pode desativar este aviso -dontwarn java.lang.management.**
-whyareyoukeeping class_specification
é uma opção útil que imprimirá o motivo pelo qual a Proguard decidiu não tocar nessa classe / método.
-verbose
- imprime logs e exceções mais detalhados
-printconfiguration
- imprime uma lista completa de opções de todos os arquivos de configuração que foram usados, incluindo regras das bibliotecas e gerados através do aapt.
-keepattributes SourceFile, LineNumberTable
- salva -keepattributes SourceFile, LineNumberTable
(nomes de arquivos, numeração de linhas) para poder depurar códigos no IDE e obter um rastreamento de pilha significativo. Certifique-se de adicionar esta opção.
Prática
Isso geralmente acontece: ative o Proguard e ele interrompe o projeto inteiro, causando muitos erros. Muitos nesta etapa desligam o Proguard e tentam não retornar a ele. Vou tentar dar algumas dicas para facilitar esse processo de transição.
Decida sobre os pontos de entrada iniciais
Se você é um desenvolvedor do Android, tudo é extremamente elementar - basta selecionar uma das duas configurações padrão do SDK do Android: proguard-android.txt
ou proguard-android-optimize.txt
, eles cuidarão de tudo que deve permanecer intocado.
Verifique todas as bibliotecas
Recentemente, mais e mais bibliotecas são distribuídas com proguard-configs prontas. O Proguard pode procurar dentro do arquivo, encontrar a configuração da biblioteca e adicioná-la a outras opções. Verifique cada biblioteca que você usa para essa configuração.

(conteúdo do arquivo aar de uma das bibliotecas)
Se você usa o Google Play Services, o plug com.google.gms.google-services
in com.google.gms.google-services
seleciona a configuração necessária por conta própria.
Se os autores da biblioteca não empacotarem a configuração no arquivo morto, talvez tenham cuidado e tenham escrito as regras em seu site, na página do repositório ou no arquivo README. Tente encontrar a configuração para a versão da biblioteca que você está usando.
Se você não encontrar nenhuma regra pronta em nenhum lugar, precisará ler os logs e resolver o problema individualmente. Provavelmente, você precisará adicionar regras de manutenção para o código da biblioteca que está quebrado. Ou ignore os erros se eles não interferirem no programa.
Inspecione seu código
Você sabe melhor qual código pode enviar embaixo da faca, mas observe cuidadosamente todos os locais onde a reflexão é usada:
- Class.forName (...) (a documentação promete que Proguard é capaz de definir esse código, no entanto, houve casos, vale a pena conferir)
- Modelos / classes de entidade que são usados na serialização, mapeamento. Todas as classes cujos nomes de campo (às vezes as próprias classes) são importantes para manter (Gson, RealmIO etc.)
- chamadas de biblioteca nativa através de JNI
Testes
Se uma classe / método for usada apenas em testes e em nenhum outro lugar, o Proguard excluirá esse código. Essa é uma situação comum se você tiver TDD :) Nesse caso, tenho uma configuração separada, onde adiciono classes que ainda não estão integradas ao projeto, não são usadas em nenhum lugar, mas precisam ser testadas.
No plug-in Android Gradle, além das instruções proguardFiles
, ainda existem testProguardFiles
. Esta instrução é necessária para especificar as configurações que serão aplicadas ao aplicativo de teste gerado para testar seu aplicativo quando você executar testes de instrumentação. Geralmente, isso é usado para obter a mesma otimização / ofuscação nos dois arquivos apk, para que não haja dessincronização entre eles. Link
Analisador de APK
O Android Studio tem uma ótima ferramenta. Você pode abri-lo através do Find Action -> Analyze APK, ou abrindo o próprio arquivo apk no Android Studio. O Analyzer mostra muitas informações úteis, mas agora estamos interessados no código. Para ver o que foi empacotado em um arquivo APK, você precisa selecionar o arquivo classes.dex

Por padrão, você verá exatamente o código resultante que passou nas etapas de redução e otimização. No entanto, você pode clicar no botão Carregar mapeamentos do programa ... , adicionar seeds.txt
e usage.txt
para ver o código que foi excluído.

Se, por algum motivo, o Proguard modificou o código necessário, localize-o no Analyzer e selecione Gerar regra de manutenção de programa através do RMB. O Analyzer gerará uma escolha de várias opções para as regras, da mais geral à mais específica, selecione UMA delas.


Para autores da biblioteca
Se você estiver criando uma biblioteca Android, poderá adicionar uma configuração de programa para seus clientes da seguinte maneira:
buildTypes { release { consumerProguardFiles 'proguard-rules.pro' } }
Na minha opinião, é melhor não otimizar e ofuscar sua biblioteca com zelo, mas proporcionar essa oportunidade aos seus clientes. Um bom tom é adicionar à configuração o que os clientes ainda terão que adicionar se incluírem o Proguard. No entanto, se você ainda deseja adicionar segurança, é óbvio que precisa proteger toda a API pulic da sua biblioteca do Proguard, incluindo descritores e assinaturas.
R8, DexGuard e Redex
R8 é a nova ferramenta do Google para substituir o atual Proguard. Espere, não tente esquecer tudo o que acabou de ler no artigo, trate-o como o novo Proguard. O Google promete manter toda a API pública, para que todas as configurações funcionem como antes. O projeto ainda está na versão beta, mas você pode tentar você mesmo.
DexGuard é um utilitário pago pelos desenvolvedores do Proguard. Pode ser usado em conjunto ou em vez de Proguard. Argumenta-se que o DexGuard pode fazer tudo o que Proguard pode fazer, mas melhor. Infelizmente, não tive chance de experimentá-lo; se alguém tiver experiência, compartilhe.
Redex é outro otimizador de dex do Facebook. É relatado que com ele você pode obter um aumento de produtividade de até 25% e reduzir o tamanho do aplicativo aplicando a ferramenta ao código já processado pela Proguard.
Em vez de uma conclusão
Não tenha medo de usar Proguard, não seja preguiçoso e gaste algum tempo configurando. Isso reduzirá seu tamanho, aumentará a velocidade do trabalho e aumentará a lealdade de seus usuários. Ao mesmo tempo, tente criar configurações eficazes do Proguard, não escreva regras de "carpete"; caso contrário, Jake Wharton bravo chegará até você e o repreenderá.

Recursos
Site da Proguard . Há também informações sobre o DexGuard.
Várias regras de exemplo
R8
Gravando uma apresentação de Como o Proguard funciona com o DroidCon
ProGuard eficaz mantém regras para gravação de apresentação de aplicativos menores (Google I / O '18)
Instruções para ativar e configurar o Proguard para Android
Página Wiki
Redex