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:
xamarin android linker

No iOS as opções ficam nas propriedades do projeto, em iOS Build:
xamarin ios linker

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:

  1. 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.
  2. 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