Vinculação no AndroidLinking on Android

Aplicativos Xamarin.Android usam um vinculador para reduzir o tamanho do aplicativo.Xamarin.Android applications use a linker to reduce the size of the application. O vinculador utiliza uma análise estática do seu aplicativo para determinar quais assemblies, tipos e membros são realmente usados.The linker employes static analysis of your application to determine which assemblies are actually used, which types are actually used, and which members are actually used. O vinculador, em seguida, se comporta como um coletor de lixo, procurando continuamente assemblies, tipos e membros que são referenciados, até todo o fechamento desses elementos ser encontrado.The linker then behaves like a garbage collector, continually looking for the assemblies, types, and members that are referenced until the entire closure of referenced assemblies, types, and members is found. Tudo fora esse fechamento é descartado.Then everything outside of this closure is discarded.

Por exemplo, a amostra Hello, Android:For example, the Hello, Android sample:

ConfiguraçãoConfiguration Tamanho 1.2.01.2.0 Size Tamanho 4.0.14.0.1 Size
Versão sem vinculação:Release without Linking: 14,0 MB14.0 MB 16,0 MB16.0 MB
Versão com vinculação:Release with Linking: 4,2 MB4.2 MB 2,9 MB2.9 MB

A vinculação resulta em um pacote que tem 30% do tamanho do pacote original (desvinculado) na 1.2.0 e 18% do pacote desvinculado na 4.0.1.Linking results in a package that is 30% the size of the original (unlinked) package in 1.2.0, and 18% of the unlinked package in 4.0.1.

ControleControl

A vinculação se baseia em análise estática.Linking is based on static analysis. Consequentemente, tudo o que depende do ambiente de tempo de execução não será detectado:Consequently, anything that depends upon the runtime environment won't be detected:

// To play along at home, Example must be in a different assembly from MyActivity.
public class Example {
    // Compiler provides default constructor...
}

[Activity (Label="Linker Example", MainLauncher=true)]
public class MyActivity {
    protected override void OnCreate (Bundle bundle)
    {
        base.OnCreate (bundle);

        // Will this work?
        var o = Activator.CreateInstance (typeof (ExampleLibrary.Example));
    }
}

Comportamento do vinculadorLinker Behavior

O mecanismo principal para controlar o vinculador é a lista suspensa Comportamento do vinculador (Vinculação no Visual Studio) dentro da caixa de diálogo Opções de Projeto.The primary mechanism for controlling the linker is the Linker Behavior (Linking in Visual Studio) drop-down within the Project Options dialog box. Há três opções:There are three options:

  1. Não Vincular (Nenhum no Visual Studio)Don't Link (None in Visual Studio)
  2. Vincular os Assemblies do SDK (Somente os Assemblies do SDK)Link SDK Assemblies (Sdk Assemblies Only)
  3. Vincular Todos os Assemblies (Assemblies de SDK e de Usuário)Link All Assemblies (Sdk and User Assemblies)

A opção Não Vincular desliga o vinculador; o exemplo de tamanho de aplicativo "Versão sem Vinculação" acima usava esse comportamento.The Don't Link option turns off the linker; the above "Release without Linking" application size example used this behavior. Isso é útil para solucionar problemas de falhas de tempo de execução, para ver se o vinculador é responsável.This is useful for troubleshooting runtime failures, to see if the linker is responsible. Essa configuração geralmente não é recomendada para builds de produção.This setting is not usually recommended for production builds.

A opção Vincular Assemblies de SDK vincula apenas assemblies que vêm com o Xamarin.Android.The Link SDK Assemblies option only links assemblies that come with Xamarin.Android. Todos os outros assemblies (tais como o seu código) não estão vinculados.All other assemblies (such as your code) are not linked.

A opção Vincular Todos os Assemblies vincula todos os assemblies, o que significa que seu código também pode ser removido se não houver nenhuma referência estática.The Link All Assemblies option links all assemblies, which means your code may also be removed if there are no static references.

O exemplo acima funcionará com as opções Não Vincular e Vincular Assemblies de SDK e falhará com o comportamento Vincular Todos os Assemblies, gerando o erro a seguir:The above example will work with the Don't Link and Link SDK Assemblies options, and will fail with the Link All Assemblies behavior, generating the following error:

E/mono    (17755): [0xafd4d440:] EXCEPTION handling: System.MissingMethodException: Default constructor not found for type ExampleLibrary.Example.
I/MonoDroid(17755): UNHANDLED EXCEPTION: System.MissingMethodException: Default constructor not found for type ExampleLibrary.Example.
I/MonoDroid(17755): at System.Activator.CreateInstance (System.Type,bool) <0x00180>
I/MonoDroid(17755): at System.Activator.CreateInstance (System.Type) <0x00017>
I/MonoDroid(17755): at LinkerScratch2.Activity1.OnCreate (Android.OS.Bundle) <0x00027>
I/MonoDroid(17755): at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (intptr,intptr,intptr) <0x00057>
I/MonoDroid(17755): at (wrapper dynamic-method) object.95bb4fbe-bef8-4e5b-8e99-ca83a5d7a124 (intptr,intptr,intptr) <0x00033>
E/mono    (17755): [0xafd4d440:] EXCEPTION handling: System.MissingMethodException: Default constructor not found for type ExampleLibrary.Example.
E/mono    (17755):
E/mono    (17755): Unhandled Exception: System.MissingMethodException: Default constructor not found for type ExampleLibrary.Example.
E/mono    (17755):   at System.Activator.CreateInstance (System.Type type, Boolean nonPublic) [0x00000] in <filename unknown>:0
E/mono    (17755):   at System.Activator.CreateInstance (System.Type type) [0x00000] in <filename unknown>:0
E/mono    (17755):   at LinkerScratch2.Activity1.OnCreate (Android.OS.Bundle bundle) [0x00000] in <filename unknown>:0
E/mono    (17755):   at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState) [0x00000] in <filename unknown>:0
E/mono    (17755):   at (wrapper dynamic-method) object:95bb4fbe-bef8-4e5b-8e99-ca83a5d7a124 (intptr,intptr,intptr)

Preservação do códigoPreserving Code

Às vezes, o vinculador removerá código que você deseja preservar.The linker will sometimes remove code that you want to preserve. Por exemplo:For example:

  • Talvez exista código que você chame dinamicamente via System.Reflection.MemberInfo.Invoke.You might have code that you call dynamically via System.Reflection.MemberInfo.Invoke.

  • Se você cria uma instância de tipos dinamicamente, convém preservar o construtor padrão de seus tipos.If you instantiate types dynamically, you may want to preserve the default constructor of your types.

  • Se você usar a serialização de XML, você talvez queira preservar as propriedades de seus tipos.If you use XML serialization, you may want to preserve the properties of your types.

Nesses casos, você pode usar o atributo Android.Runtime.Preserve.In these cases, you can use the Android.Runtime.Preserve attribute. Cada membro que não está vinculado estaticamente pelo aplicativo está sujeito a ser removido, então esse atributo pode ser usado para marcar membros que não estão referenciados estaticamente, mas ainda são necessários para seu aplicativo.Every member that is not statically linked by the application is subject to be removed, so this attribute can be used to mark members that are not statically referenced but are still needed by your application. Você pode aplicar esse atributo a todos os membros de um tipo ou ao tipo propriamente dito.You can apply this attribute to every member of a type, or to the type itself.

No exemplo a seguir, esse atributo é usado para preservar o construtor da classe Example:In the following example, this attribute is used to preserve the constructor of the Example class:

public class Example
{
    [Android.Runtime.Preserve]
    public Example ()
    {
    }
}

Se você quiser preservar o tipo inteiro, poderá usar a seguinte sintaxe de atributo:If you want to preserve the entire type, you can use the following attribute syntax:

[Android.Runtime.Preserve (AllMembers = true)]

Por exemplo, no fragmento de código a seguir, toda a classe Example é preservada para a serialização de XML:For example, in the following code fragment the entire Example class is preserved for XML serialization:

[Android.Runtime.Preserve (AllMembers = true)]
class Example
{
    // Compiler provides default constructor...
}

Às vezes você deseja preservar a certos membros, mas somente se o tipo recipiente foi preservado.Sometimes you want to preserve certain members, but only if the containing type was preserved. Nesses casos, use a seguinte sintaxe de atributo:In those cases, use the following attribute syntax:

[Android.Runtime.Preserve (Conditional = true)]

Se você não desejar executar uma dependência em bibliotecas do Xamarin – por exemplo, você está criando uma PCL (biblioteca de classes portátil) de plataforma cruzada – você ainda pode usar o atributo Android.Runtime.Preserve.If you do not want to take a dependency on the Xamarin libraries – for example, you are building a cross platform portable class library (PCL) – you can still use the Android.Runtime.Preserve attribute. Para fazer isso, declare uma classe PreserveAttribute dentro do namespace Android.Runtime deste modo:To do this, declare a PreserveAttribute class within the Android.Runtime namespace like this:

namespace Android.Runtime
{
    public sealed class PreserveAttribute : System.Attribute
    {
        public bool AllMembers;
        public bool Conditional;
    }
}

Nos exemplos acima, o atributo Preserve é declarado no namespace Android.Runtime; no entanto, você pode usar o atributo Preserve em qualquer namespace porque o vinculador procura esse atributo pelo nome do tipo.In the above examples, the Preserve attribute is declared in the Android.Runtime namespace; however, you can use the Preserve attribute in any namespace because the linker looks up this attribute by type name.

falseflagfalseflag

Se o atributo [Preserve] não pode ser usado, geralmente é útil fornecer um bloco de código para que o vinculador acredite que o tipo é usado, impedindo simultaneamente que o bloco de código seja executado em tempo de execução.If the [Preserve] attribute can't be used, it is often useful to provide a block of code so that the linker believes that the type is used, while preventing the block of code from being executed at runtime. Para usar essa técnica, poderíamos fazer:To make use of this technique, we could do:

[Activity (Label="Linker Example", MainLauncher=true)]
class MyActivity {

#pragma warning disable 0219, 0649
    static bool falseflag = false;
    static MyActivity ()
    {
        if (falseflag) {
            var ignore = new Example ();
        }
    }
#pragma warning restore 0219, 0649

    // ...
}

linkskiplinkskip

É possível especificar que um conjunto de assemblies fornecidos pelo usuário não sejam vinculados, permitindo simultaneamente que outros assemblies de usuário sejam ignorados com o comportamento Vincular Assemblies de SDK usando a Propriedade AndroidLinkSkip do MSBuild:It is possible to specify that a set of user-provided assemblies should not be linked at all, while allowing other user assemblies to be skipped with the Link SDK Assemblies behavior by using the AndroidLinkSkip MSBuild property:

<PropertyGroup>
    <AndroidLinkSkip>Assembly1;Assembly2</AndroidLinkSkip>
</PropertyGroup>

LinkDescriptionLinkDescription

A @(LinkDescription) Ação de build pode ser usada em arquivos que podem conter um arquivo de configuração de vinculador personalizado.The @(LinkDescription) Build action may be used on files which can contain a Custom linker configuration file. arquivo.file. Arquivos de configuração de vinculador personalizados podem ser necessários para preservar membros internal ou private que precisam ser preservados.Custom linker configuration files may be required to preserve internal or private members that need to be preserved.

Atributos personalizadosCustom Attributes

Quando um assembly é vinculado, os seguintes tipos de atributo personalizados serão removidos de todos os membros:When an assembly is linked, the following custom attribute types will be removed from all members:

  • System.ObsoleteAttributeSystem.ObsoleteAttribute
  • System.MonoDocumentationNoteAttributeSystem.MonoDocumentationNoteAttribute
  • System.MonoExtensionAttributeSystem.MonoExtensionAttribute
  • System.MonoInternalNoteAttributeSystem.MonoInternalNoteAttribute
  • System.MonoLimitationAttributeSystem.MonoLimitationAttribute
  • System.MonoNotSupportedAttributeSystem.MonoNotSupportedAttribute
  • System.MonoTODOAttributeSystem.MonoTODOAttribute
  • System.Xml.MonoFIXAttributeSystem.Xml.MonoFIXAttribute

Quando um assembly é vinculado, os seguintes tipos de atributo personalizados serão removidos de todos os builds de versão:When an assembly is linked, the following custom attribute types will be removed from all members in Release builds:

  • System.Diagnostics.DebuggableAttributeSystem.Diagnostics.DebuggableAttribute
  • System.Diagnostics.DebuggerBrowsableAttributeSystem.Diagnostics.DebuggerBrowsableAttribute
  • System.Diagnostics.DebuggerDisplayAttributeSystem.Diagnostics.DebuggerDisplayAttribute
  • System.Diagnostics.DebuggerHiddenAttributeSystem.Diagnostics.DebuggerHiddenAttribute
  • System.Diagnostics.DebuggerNonUserCodeAttributeSystem.Diagnostics.DebuggerNonUserCodeAttribute
  • System.Diagnostics.DebuggerStepperBoundaryAttributeSystem.Diagnostics.DebuggerStepperBoundaryAttribute
  • System.Diagnostics.DebuggerStepThroughAttributeSystem.Diagnostics.DebuggerStepThroughAttribute
  • System.Diagnostics.DebuggerTypeProxyAttributeSystem.Diagnostics.DebuggerTypeProxyAttribute
  • System.Diagnostics.DebuggerVisualizerAttributeSystem.Diagnostics.DebuggerVisualizerAttribute