Uma das perguntas que mais ouço nas minhas palestras a respeito de Xamarin é sobre o impacto dele no tamanho final do app. Muita vezes um “Hello World” começa com 15 mb e isso assusta muita gente. E tá certo, todas lojas de apps te lembram da importância de um app pequeno para que você não perca downloads de usuários na 3G/4G. Pensando em evitar esse tamanho excessivo, a Xamarin utiliza o Linker, para deixar o seu app do menor tamanho possível. O problema é que nem sempre ativar o Linker é tão fácil, principalmente quando se usa bibliotecas de terceiros, como o Prism.
Aplicativos Xamarin começam com esse tamanho porque seu app agora tem todo o subset de bibliotecas do Mono para Android ou iOS (dependendo da plataforma usada) à sua disposição, isso inevitavelmente aumenta o tamanho do app. Mas seu app dificilmente estará usando todas as bibliotecas disponíveis, então por que pagar o preço de ter todo esse tamanho adicionado ao seu app? Pensando justamente nisso é que a Xamarin facilitou a utilização do Mono Linker.
Por padrão o Linker vem desabilitado no modo Debug. E é por isso que o seu “Hello World” tem todo esse tamanho, porque você está olhando para uma versão que não deveria ser utilizada para distribuição na loja.
O que é o Linker?
O Linker é uma ferramenta open source feita pela equipe do Mono que detecta códigos não utilizados e os removem dos artefatos gerados, isso permite diminuir o tamanho do seu app consideravelmente. Todo tooling de Xamarin permite que ativemos o Link de maneira muito simples, e isso deve ser feito por plataforma e por build configuration. Isso significa que o linker pode ser configurado de forma diferente entre Release|iPhone
e Release|iPhoneSimulator
, por exemplo.
No Android as opções do Linker ficam nas propriedades do projeto, em Android Build:
No iOS as opções ficam nas propriedades do projeto, em iOS Build:
O Linker possui 3 configurações diferentes:
- Don’t Link (None no Visual Studio) – Não executa o Linker e não diminui o seu app. Nunca utilize essa opção em produção.
- Link SDK Assemblies (Sdk Assemblies Only) – Remove apenas códigos não utilizados do Xamarin.Android e Xamarin.iOS, não remove nada de códigos escritos por você nem de pacotes NuGet de terceiros.
- Link All Assemblies (Sdk and User Assemblies) – Remove todo código não utilizado, inclusive seu e de pacotes NuGet de terceiros. Essa opção gera o menor binário possível.
A opção “Link All” parece ser a melhor de todas, mas por que ela não vem habilitada por padrão? Existem dois motivos principais:
- Rodar o Linker adiciona um tempo a mais na compilação do seu app. Muitas vezes o tempo é bem pequeno, mas tudo depende da complexidade do seu app.
- O Linker não consegue detectar códigos utilizados através de reflections e afins. Isso faz com que ele acabe removendo código que você está utilizando, fazendo seu app parar de funcionar.
O segundo item é o que normalmente afasta as pessoas de utilizar o Linker para gerar versões de produção, que deveriam ir para a loja. Mas é claro que existe solução para isso. É possível instrumentar o Linker para manter código que ele tenha removido equivocadamente, isso está documentado aqui e funciona muito bem para códigos que você tenha feito, mas para bibliotecas externas, é um pouco mais chato, pois você precisa descobrir quais classes foram removidas para adicioná-las às regras de exceção, conforme descritas aqui.
Custom Linker para Xamarin.Forms com Prism
Recentemente eu ativei o modo “Link All” para Android e iOS nos meus apps Xamarin.Forms utilizando Prism. O Prism utiliza reflection em algumas partes do código o que tornou essa tarefa trabalhosa de fazer, pois envolveu uma dinâmica “gato e rato” para descobrir todas as classes que o Linker removeu equivocadamente. Para facilitar a vida de quem tiver que fazer isso, estou compartilhando o arquivo linker.xml
final que precisei adicionar aos meus projetos Android e iOS para que tudo funcionasse.
Lembre-se de seguir as instruções dessa página para ativar o Build Action correto deste arquivo no seu projeto. Só adicinar o arquivo no projeto não vai executar as regras do arquivo.
<?xml version="1.0" encoding="UTF-8" ?> <linker> <!-- No meu projeto utilizei o container Unity (padrão nos templates antigos do Prism) --> <assembly fullname="Microsoft.Practices.Unity"> <type fullname="Microsoft.Practices.Unity*" /> </assembly> <assembly fullname="Prism.Unity.Forms"> <type fullname="Prism.Unity.Extensions.DependencyServiceExtension" preserve="all"> <method name=".ctor" /> </type> <type fullname="Prism.Unity.Navigation.UnityPageNavigationService" preserve="all" /> </assembly> <assembly fullname="Prism.Forms"> <type fullname="Prism.Common.ApplicationProvider" preserve="all" /> <type fullname="Prism.Services.PageDialogService" preserve="all" /> <type fullname="Prism.Services.DeviceService" preserve="all" /> </assembly> </linker>
Preste atenção que no meu caso eu utilizo o Unity como container de injeção de dependências com o Prism. Se você estiver utilizando outro container, as regras relacionadas mudarão.
Este arquivo não contém todo o meu linker.xml
, eu estou compartilhando apenas as partes que se referem ao Prism, que serão reusáveis para outros projetos, você ainda pode ter que adicionar regras para evitar que o seu código seja removido.
Conclusão
É recomendado sempre ter o Linker habilitado para os builds de release do seu app, esse post deve te ajudar a solucionar os maiores problemas quando seu app utiliza Xamarin.Forms com Prism.
(Cross-post de: http://high5devs.com/2017/11/habilitando-linker-em-projetos-xamarin-forms-com-prism/)
Foto usada no post: Pixabay
Mahmoud Ali
Desenvolvedor de Software na Lambda3, Microsoft MVP, amante de um bom café ☕️ e uma boa cerveja 🍺.