Propojení v Androidu

Aplikace Xamarin.Android používají linker ke zmenšení velikosti aplikace. Linker využívá statickou analýzu vaší aplikace k určení, která sestavení se skutečně používají, které typy se skutečně používají a které členy se skutečně používají. Linker se pak chová jako uvolňování paměti, průběžně hledá sestavení, typy a členy, na které se odkazuje, dokud se nenajde celé uzavření odkazovaných sestavení, typů a členů. Pak se všechno mimo toto uzavření zahodí.

Například ukázka Hello, Android :

Konfigurace Velikost 1.2.0 Velikost 4.0.1
Vydání bez propojení: 14,0 MB 16,0 MB
Vydání s propojením: 4,2 MB 2,9 MB

Propojení vede k vytvoření balíčku o velikosti 30 % původního (nepřipojeného) balíčku ve verzi 1.2.0 a 18 % nepřipojeného balíčku ve verzi 4.0.1.

Ovládací prvek

Propojení je založeno na statické analýze. V důsledku toho se nezjistí nic, co závisí na prostředí modulu runtime:

// 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));
    }
}

Chování linkeru

Primárním mechanismem pro řízení linkeru je rozevírací seznam Chování linkeru (propojení v sadě Visual Studio) v dialogovém okně Možnosti projektu. K dispozici jsou tři možnosti:

  1. Nevypojovat (žádný v sadě Visual Studio)
  2. Propojení sestavení sady SDK (pouze sestavení sdk)
  3. Propojit všechna sestavení (sdk a uživatelská sestavení)

Možnost Ne linker vypne. Výše uvedený příklad verze bez propojení aplikace použil toto chování. To je užitečné pro řešení potíží se selháním modulu runtime, abyste zjistili, jestli je linker zodpovědný. Toto nastavení se obvykle nedoporučuje pro produkční sestavení.

Možnost Propojit sestavení sady SDK pouze propojí sestavení, která jsou součástí Xamarin.Android. Všechna ostatní sestavení (například váš kód) nejsou propojená.

Možnost Propojit všechna sestavení propojí všechna sestavení, což znamená, že kód může být také odebrán, pokud neexistují žádné statické odkazy.

Výše uvedený příklad bude fungovat s možnostmi Ne linkovat a propojit sestavení sady SDK a selže s chováním Propojit všechna sestavení a vygeneruje následující chybu:

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)

Zachování kódu

Linker někdy odebere kód, který chcete zachovat. Příklad:

  • Můžete mít kód, který voláte dynamicky prostřednictvím System.Reflection.MemberInfo.Invoke.

  • Pokud vytváříte instance typů dynamicky, možná budete chtít zachovat výchozí konstruktor typů.

  • Pokud používáte serializaci XML, můžete chtít zachovat vlastnosti vašich typů.

V těchto případech můžete použít atribut Android.Runtime.Preserve . Každý člen, který není staticky propojený aplikací, je předmětem odebrání, takže tento atribut lze použít k označení členů, které nejsou staticky odkazovány, ale jsou stále potřeba vaší aplikací. Tento atribut můžete použít u každého člena typu nebo u samotného typu.

V následujícím příkladu se tento atribut používá k zachování konstruktoru Example třídy:

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

Pokud chcete zachovat celý typ, můžete použít následující syntaxi atributu:

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

Například v následujícím fragmentu kódu je zachována celá Example třída pro serializaci XML:

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

Někdy chcete zachovat určité členy, ale pouze v případě, že byl zachován typ obsahující. V těchto případech použijte následující syntaxi atributu:

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

Pokud nechcete využívat závislost na knihovnách Xamarinu – například vytváříte knihovnu přenosných tříd pro různé platformy (PCL), můžete atribut stále používat Android.Runtime.Preserve . Chcete-li to provést, deklarujte PreserveAttribute třídu v rámci Android.Runtime oboru názvů takto:

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

Ve výše uvedených příkladech Preserve je atribut deklarován v Android.Runtime oboru názvů. Atribut však můžete použít Preserve v jakémkoli oboru názvů, protože linker vyhledá tento atribut podle názvu typu.

falseflag

Pokud atribut [Preserve] nejde použít, je často užitečné poskytnout blok kódu, aby linker věřil, že se typ používá, a zároveň brání spuštění bloku kódu za běhu. Abychom mohli tuto techniku využít, mohli bychom:

[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

    // ...
}

linkskip

Pomocí vlastnosti AndroidLinkSkip MSBuild je možné určit, že sada uživatelských sestavení by neměla být vůbec propojena, zatímco ostatní uživatelská sestavení se přeskočí s chováním propojení sestavení SDK pomocí vlastnosti AndroidLinkSkip MSBuild:

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

Popis odkazu

Akci @(LinkDescription) sestavení lze použít u souborů, které mohou obsahovat vlastní konfigurační soubor linkeru. . Vlastní konfigurační soubory linkeru můžou být nutné k zachování internal nebo private zachování členů, které je potřeba zachovat.

Vlastní atributy

Při propojení sestavení budou ze všech členů odebrány následující typy vlastních atributů:

  • System.ObsoleteAttribute
  • System.MonoDocumentationNoteAttribute
  • System.MonoExtensionAttribute
  • System.MonoInternalNoteAttribute
  • System.MonoLimitationAttribute
  • System.MonoNotSupportedAttribute
  • System.MonoTODOAttribute
  • System.Xml.MonoFIXAttribute

Při propojení sestavení se ze všech členů sestavení v buildech vydané verze odeberou následující vlastní typy atributů:

  • System.Diagnostics.DebuggableAttribute
  • System.Diagnostics.DebuggerBrowsableAttribute
  • System.Diagnostics.DebuggerDisplayAttribute
  • System.Diagnostics.DebuggerHiddenAttribute
  • System.Diagnostics.DebuggerNonUserCodeAttribute
  • System.Diagnostics.DebuggerStepperBoundaryAttribute
  • System.Diagnostics.DebuggerStepThroughAttribute
  • System.Diagnostics.DebuggerTypeProxyAttribute
  • System.Diagnostics.DebuggerVisualizerAttribute