Propojení v Androidu

Aplikace Xamarin.Android používají linker ke zmenšení velikosti aplikace. Linker využívá statickou analýzu 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, neustále 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í.

Příklad Hello, Android :

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

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

Řízení

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

// 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 Linker Behavior (Propojení v sadě Visual Studio) v dialogovém okně Možnosti projektu . Existují tři možnosti:

  1. Ne linkovat (žá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 velikosti aplikace Release without Linking použil toto chování. To je užitečné při řešení potíží se selháními 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 v Xamarin.Androidu. 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 Nepřipojovat 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. Napří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, možná budete 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 pro každého člena typu nebo pro samotný typ.

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 atributů:

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

Například v následujícím fragmentu Example kódu je celá třída zachována 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řenositelných tříd (PCL) pro různé platformy , můžete atribut stále používat Android.Runtime.Preserve . Chcete-li to provést, deklarujte PreserveAttribute třídu v oboru Android.Runtime názvů takto:

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

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

falseflag

Pokud atribut [Zachovat] nelze 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 sestavení sady Link SDK:

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

Popis LinkDescription

@(LinkDescription)Akci Sestavení lze použít u souborů, které můžou obsahovat konfigurační soubor vlastního linkeru. . K zachování nebo private zachování členů, které je potřeba zachovatinternal, můžou být potřeba vlastní konfigurační soubory linkeru.

Vlastní atributy

Při propojení sestavení se ze všech členů odeberou 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í typy vlastních 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