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í.
Příklad ukázky Hello pro 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:
- Nevypojovat (žádný v sadě Visual Studio)
- Propojení sestavení sady SDK (pouze sestavení sdk)
- 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