Episódio I - Reduzindo pacotes Xamarin.Android

Publicado em 29/04/2016

Cover

Episódio I - Reduzindo pacotes Xamarin.Android

Observando as comunidades brasileiras de Xamarin desde a aquisição da Microsoft, percebe-se que uma grande quantidade de pessoas estão começando a estudar a plataforma (afinal agora existe a possibilidade de aprender sem pagar nada!). Em resposta a isso, vou começar uma nova série, voltada para novatos, que buscará responder dúvidas mais básicas e ensinar o caminho das pedras dessa fantástica plataforma que é o Xamarin. Esse é o primeiro post dessa série. No post de hoje eu irei falar sobre um tema crucial para qualquer desenvolvedor Xamarin: o tamanho do .apk.

Como assim reduzir os pacotes?

Bom, vamos por partes. Xamarin permite que você escreva em C# e execute no Android. Tendo em vista que esse é um post para iniciantes eu não vou entrar nos detalhes de funcionamento da plataforma. É necessário, porém, entender um pouco do que o Xamarin faz para permitir que seu código C# rode em um dispositivo Android.

Você já deve saber que o C# é uma linguagem que roda em cima de uma máquina virtual que compila o seu código em tempo de execução. Essa máquina virtual pode ser o CLR, da Microsoft, ou o Mono, um projeto open source que busca fazer o mesmo em outros sistemas operacionais. O que acontece debaixo dos panos para o seu código C# rodar é uma versão do Mono sendo executada em um nível muito baixo (em cima do Kernel Linux do dispositivo).

“E dai?” você me pergunta. O problema disso é que toda aplicação Xamarin.Android precisa levar para o dispositivo, além de todo o código que você escreveu, o Mono Runtime, que é o que permite a execução daquele código. Como apps escritos em Java não precisam disso, aplicativos escritos em Xamarin tendem a ser maiores que versões escritas em Java.

Esse Mono Runtime faz um Hello World ter 100MB?

Isso tudo também não! O Mono e mais as bibliotecas necessárias para permitir o uso do .net framework no Android totalizam quase 16MB, de acordo com a própria Xamarin. Como você pode ver na imagem abaixo, é completamente absurdo que um app de 6KB pese 16MB só porque foi feito em Xamarin!

Pré linker

É ai que entra o “Linker”. O Linker é uma ferramenta que analísa o seu código e remove partes que não estão sendo utilizadas. No modo mais tranquilo, o Linker é executado em cima dos Assemblies necessários para o funcionamento do Mono.Android. Como você pode ver, só de habilitar o linker o pacote da última imagem fica assim:

Pós-linker

No modo mais agressivo, o linker é executado em cima de todos os assemblies. Isso inclui os assemblies já mencionados, o seu código e também o código de pacotes Nuget que você esteja usando na sua aplicação. Para habilitar o linker, vá nas opções do seu projeto > Android Build > Linker. As três opções de uso do linker são “Don’t Link”, “Link SDK Assemblies” e “Link All Assemblies”, que representam, respectivamente, não remover nada, remover só o código do runtime e remover código de todos assemblies. É inadmissível que um apk seja publicado sem usar pelo menos a opção “Link SDK Assemblies”, então certifique-se de habilita-la antes de colocar seu app na loja.

E se o Linker cortar coisa demais?

Como eu disse, a opção “Link All Assemblies” é bastante agressiva. Como a remoção de código é baseada em uma análise estática, pode ser que algumas partes do seu código sejam removidas por engano. Isso acontece se você estiver usando reflection, por exemplo. Ao usar reflection para construir uma classe, pode ser que o construtor dessa classe não seja usado em lugar nenhum do seu projeto. Como o linker não vê o construtor sendo usado, ele o remove. Durante a execução do seu app, as apis de reflection tentam achar o construtor e isso causa uma Exception.

Para evitar esse comportamento você tem algumas opção. A primeira e mais simples é anotar o código que não é usado diretamente mas precisa ser preservado usando o atributo Preserve. Isso notifica o linker de que essa classe ou método precisa ficar onde está para o funcionamento do app. O atributo conta ainda com as opção AllMembers, que quando usada em uma classe preserva todos os seu membros, e [Conditional](http://androidapi.xamarin.com/api/field/Android.Runtime.PreserveAttribute.Conditional/, que preserva um membro somente se o tipo que o contém for preservado também.

Para casos onde você não pode usar esse atributo (como PCLs ou código em pacotes nuget de terceiros), você pode recorrer ao linkskip (que é configurado em Options > Android > Linker > Skip linking assemblies). Essa opção recebe uma lista de assemblies, com os nomes separados por um ponto e vírgula (;), e indica ao linker que esses assemblies devem ser ignorados.

Se você quer preservar algo mas não quer preservar o assembly inteiro, há uma outra opção. Essa opção é criar um arquivo que “finge” utilizar o que você quer manter. Precisa ser código válido e ficar em uma classe que será compilada dentro do seu projeto, mas o método não precisa ser chamado. Ele só precisa estar lá. Isso engana o linker, fazendo com que ele pense que o método é usado (afinal não tem como definir isso em uma análise estática) e o método é mantigo. Um exemplo disso pode ser visto no arquivo LinkerPleaseInclude, do projeto MvvmCross. Como você pode ver, o arquivo cria várias overloads do método Include. Esses métodos não são chamados em nenhum lugar, mas eles servem para manter as propriedades e métodos necessários ao funcionamento do Framework, mesmo quando o linker é habilitado!

E além do Xamarin, quais as dicas para Android?

Um dica é usar a opção “Generate one package per selected ABI” e na aba advanced escolher quais arquiteturas você vai suportar. Isso ajuda pois o compilador criará um apk para cada versão suportada. Na hora de subir para loja, você sobe todos os arquivos (que são um pouco menores que o arquivo único) e o usuário final baixa apenas o que for relevante para a arquitetura do celular dele.

Outra dica é utilizar o programa PNGGauntlet em todos os seus resources. Este programa remove informações não utilizadas das imagens, o que pode acarretar em reduções significativas no tamanho do seu app. Nem preciso dizer que remover imagens e arquivos não utilizados é uma excelente prática.

Posso pegar um certificado de conclusão de curso agora?

Ainda não, ainda tem muita coisa nessa série. Diminuir o seu pacote é praticamente uma arte! É um processo que requer uma análise do seu projeto para definir a melhor estratégia. Contudo, ele não é nenhum bicho de sete cabeças, então nada de publicar apps gigantes por pura preguiça!

Sim, eu pretendo fazer um post desses sobre iOS :)

blog comments powered by Disqus