Udostępnij za pośrednictwem


Łączenie w systemie Android

Aplikacje platformy Xamarin.Android używają konsolidatora , aby zmniejszyć rozmiar aplikacji. Konsolidator wykorzystuje analizę statyczną aplikacji w celu określenia, które zestawy są rzeczywiście używane, które typy są rzeczywiście używane i które elementy członkowskie są rzeczywiście używane. Konsolidator zachowuje się jak moduł odśmiecacza pamięci, stale wyszukuje zestawy, typy i elementy członkowskie, do których odwołuje się całe zamknięcie odwołanych zestawów, typów i elementów członkowskich. Następnie wszystkie elementy poza tym zamknięciem zostaną odrzucone.

Na przykład przykład Hello Android:

Konfigurowanie Rozmiar 1.2.0 Rozmiar 4.0.1
Wydanie bez łączenia: 14,0 MB 16,0 MB
Wydanie z łączeniem: 4,2 MB 2,9 MB

Łączenie powoduje utworzenie pakietu o rozmiarze 30% oryginalnego (odłączonego) pakietu w wersji 1.2.0 i 18% niezałączonego pakietu w wersji 4.0.1.

Kontrolka

Łączenie jest oparte na analizie statycznej. W związku z tym wszystkie elementy zależne od środowiska uruchomieniowego nie zostaną wykryte:

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

Zachowanie konsolidatora

Podstawowym mechanizmem kontrolowania konsolidatora jest lista rozwijana Zachowanie konsolidatora (łączenie w programie Visual Studio) w oknie dialogowym Opcje projektu. Dostępne są trzy opcje:

  1. Nie łącz (brak w programie Visual Studio)
  2. Łączenie zestawów SDK (tylko zestawy sdk)
  3. Łączenie wszystkich zestawów (zestawów SDK i zestawów użytkownika)

Opcja Nie łącz wyłącza konsolidatora; powyższy przykład rozmiaru aplikacji "Zwolnij bez łączenia" użył tego zachowania. Jest to przydatne w przypadku rozwiązywania problemów z błędami środowiska uruchomieniowego, aby sprawdzić, czy konsolidator jest odpowiedzialny. To ustawienie nie jest zwykle zalecane w przypadku kompilacji produkcyjnych.

Opcja Zestawy zestawu SDK linków łączy tylko zestawy, które są dostarczane z platformą Xamarin.Android. Wszystkie inne zestawy (takie jak kod) nie są połączone.

Opcja Połącz wszystkie zestawy łączy wszystkie zestawy, co oznacza, że kod może zostać również usunięty, jeśli nie ma żadnych odwołań statycznych.

Powyższy przykład będzie działać z opcjami Nie łącz i połącz zestawów SDK i zakończy się niepowodzeniem z zachowaniem Połącz wszystkie zestawy , generując następujący błąd:

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)

Zachowywanie kodu

Konsolidator czasami usuwa kod, który chcesz zachować. Na przykład:

  • Może istnieć kod, który jest wywoływany dynamicznie za pomocą polecenia System.Reflection.MemberInfo.Invoke.

  • Jeśli tworzysz wystąpienia typów dynamicznie, możesz zachować domyślny konstruktor typów.

  • Jeśli używasz serializacji XML, możesz zachować właściwości typów.

W takich przypadkach można użyć atrybutu Android.Runtime.Preserve . Każdy element członkowski, który nie jest statycznie połączony przez aplikację, może zostać usunięty, więc ten atrybut może służyć do oznaczania elementów członkowskich, do których nie odwołuje się statycznie, ale są nadal potrzebne przez aplikację. Ten atrybut można zastosować do każdego elementu członkowskiego typu lub do samego typu.

W poniższym przykładzie ten atrybut jest używany do zachowania konstruktora Example klasy:

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

Jeśli chcesz zachować cały typ, możesz użyć następującej składni atrybutu:

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

Na przykład w poniższym fragmentcie kodu cała Example klasa jest zachowywana dla serializacji XML:

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

Czasami chcesz zachować niektóre elementy członkowskie, ale tylko wtedy, gdy typ zawierający został zachowany. W takich przypadkach użyj następującej składni atrybutu:

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

Jeśli nie chcesz stosować zależności od bibliotek platformy Xamarin — na przykład tworzysz międzyplatformową przenośną bibliotekę klas (PCL) — nadal możesz użyć atrybutu Android.Runtime.Preserve . W tym celu zadeklaruj klasę PreserveAttribute w Android.Runtime przestrzeni nazw w następujący sposób:

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

W powyższych przykładach atrybut jest zadeklarowany w Android.Runtime przestrzeni nazw. Można jednak użyć atrybutu Preserve w dowolnej przestrzeni nazw, Preserve ponieważ konsolidator wyszukuje ten atrybut według nazwy typu.

falseflag

Jeśli nie można użyć atrybutu [Preserve], często przydaje się podanie bloku kodu, dzięki czemu konsolidator uważa, że typ jest używany, uniemożliwiając jednocześnie wykonywanie bloku kodu w czasie wykonywania. Aby skorzystać z tej techniki, możemy wykonać następujące czynności:

[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

Istnieje możliwość określenia, że zestaw zestawów dostarczonych przez użytkownika nie powinien być w ogóle połączony, jednocześnie zezwalając innym użytkownikom na pomijanie zestawów zestawu SDK linków za pomocą właściwości AndroidLinkSkip MSBuild:

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

LinkDescription

Akcja Kompilacja@(LinkDescription) może być używana w plikach, które mogą zawierać plik konfiguracji konsolidatora niestandardowego. Boot.ini. Pliki konfiguracji konsolidatora niestandardowego mogą być wymagane do zachowania internal elementów członkowskich lub private elementów członkowskich, które muszą zostać zachowane.

Atrybuty niestandardowe

Po połączeniu zestawu następujące typy atrybutów niestandardowych zostaną usunięte ze wszystkich elementów członkowskich:

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

Po połączeniu zestawu następujące typy atrybutów niestandardowych zostaną usunięte ze wszystkich elementów członkowskich w kompilacjach wydania:

  • 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