Menautkan di Android
Aplikasi Xamarin.Android menggunakan linker untuk mengurangi ukuran aplikasi. Linker menggunakan analisis statis aplikasi Anda untuk menentukan rakitan mana yang benar-benar digunakan, jenis mana yang benar-benar digunakan, dan anggota mana yang benar-benar digunakan. Linker kemudian bertingkah seperti pengumpul sampah, terus mencari rakitan, jenis, dan anggota yang dirujuk sampai seluruh penutupan rakitan, jenis, dan anggota yang direferensikan ditemukan. Kemudian segala sesuatu di luar penutupan ini dibuang.
Misalnya, sampel Hello Android:
Konfigurasi | 1.2.0 Ukuran | 4.0.1 Ukuran |
---|---|---|
Rilis tanpa Menautkan: | 14,0 MB | 16.0 MB |
Rilis dengan Menautkan: | 4.2 MB | 2,9 MB |
Penautan menghasilkan paket yang 30% ukuran paket asli (tidak tertaut) dalam 1,2,0, dan 18% dari paket yang tidak ditautkan dalam 4.0.1.
Menguasai
Penautan didasarkan pada analisis statis. Akibatnya, apa pun yang bergantung pada lingkungan runtime tidak akan terdeteksi:
// 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));
}
}
Perilaku Linker
Mekanisme utama untuk mengontrol linker adalah menu drop-down Perilaku Linker (Menautkan di Visual Studio) dalam kotak dialog Opsi Proyek. Ada tiga opsi:
- Jangan Tautkan (Tidak Ada di Visual Studio)
- Menautkan Rakitan SDK (Hanya Rakitan Sdk)
- Tautkan Semua Rakitan (Sdk dan Rakitan Pengguna)
Opsi Jangan Tautkan menonaktifkan linker; contoh ukuran aplikasi "Rilis tanpa Penautan" di atas menggunakan perilaku ini. Ini berguna untuk memecahkan masalah kegagalan runtime, untuk melihat apakah linker bertanggung jawab. Pengaturan ini biasanya tidak disarankan untuk build produksi.
Opsi Tautan SDK Assemblies hanya menautkan rakitan yang dilengkapi dengan Xamarin.Android. Semua rakitan lainnya (seperti kode Anda) tidak ditautkan.
Opsi Tautkan Semua Rakitan menautkan semua rakitan, yang berarti kode Anda juga dapat dihapus jika tidak ada referensi statis.
Contoh di atas akan berfungsi dengan opsi Jangan Tautkan dan Tautkan Rakitan SDK, dan akan gagal dengan perilaku Tautkan Semua Rakitan, menghasilkan kesalahan berikut:
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)
Mempertahankan Kode
Linker terkadang akan menghapus kode yang ingin Anda pertahankan. Contohnya:
Anda mungkin memiliki kode yang Anda panggil secara dinamis melalui
System.Reflection.MemberInfo.Invoke
.Jika Anda membuat instans jenis secara dinamis, Anda mungkin ingin mempertahankan konstruktor default jenis Anda.
Jika Anda menggunakan serialisasi XML, Anda mungkin ingin mempertahankan properti jenis Anda.
Dalam kasus ini, Anda dapat menggunakan atribut Android.Runtime.Preserve . Setiap anggota yang tidak ditautkan secara statis oleh aplikasi tunduk pada penghapusan, sehingga atribut ini dapat digunakan untuk menandai anggota yang tidak direferensikan secara statis tetapi masih diperlukan oleh aplikasi Anda. Anda dapat menerapkan atribut ini ke setiap anggota jenis, atau ke jenis itu sendiri.
Dalam contoh berikut, atribut ini digunakan untuk mempertahankan konstruktor Example
kelas:
public class Example
{
[Android.Runtime.Preserve]
public Example ()
{
}
}
Jika Anda ingin mempertahankan seluruh jenis, Anda bisa menggunakan sintaks atribut berikut:
[Android.Runtime.Preserve (AllMembers = true)]
Misalnya, dalam fragmen kode berikut, seluruh Example
kelas dipertahankan untuk serialisasi XML:
[Android.Runtime.Preserve (AllMembers = true)]
class Example
{
// Compiler provides default constructor...
}
Terkadang Anda ingin mempertahankan anggota tertentu, tetapi hanya jika jenis yang berisi dipertahankan. Dalam kasus tersebut, gunakan sintaks atribut berikut:
[Android.Runtime.Preserve (Conditional = true)]
Jika Anda tidak ingin mengambil dependensi pada pustaka Xamarin - misalnya, Anda sedang membangun pustaka kelas portabel lintas platform (PCL) - Anda masih dapat menggunakan Android.Runtime.Preserve
atribut . Untuk melakukan ini, deklarasikan PreserveAttribute
kelas dalam Android.Runtime
namespace seperti ini:
namespace Android.Runtime
{
public sealed class PreserveAttribute : System.Attribute
{
public bool AllMembers;
public bool Conditional;
}
}
Dalam contoh di atas, Preserve
atribut dideklarasikan di Android.Runtime
namespace; namun, Anda dapat menggunakan Preserve
atribut di namespace layanan apa pun karena linker mencari atribut ini berdasarkan nama jenis.
falseflag
Jika atribut [Pertahankan] tidak dapat digunakan, sering kali berguna untuk menyediakan blok kode sehingga linker percaya bahwa jenis tersebut digunakan, sambil mencegah blok kode dijalankan pada runtime. Untuk menggunakan teknik ini, kita dapat melakukan:
[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
Dimungkinkan untuk menentukan bahwa sekumpulan rakitan yang disediakan pengguna tidak boleh ditautkan sama sekali, sementara memungkinkan rakitan pengguna lain dilewati dengan perilaku Rakitan Link SDK dengan menggunakan properti AndroidLinkSkip MSBuild:
<PropertyGroup>
<AndroidLinkSkip>Assembly1;Assembly2</AndroidLinkSkip>
</PropertyGroup>
LinkDescription
Tindakan @(LinkDescription)
Build dapat digunakan pada file yang dapat berisi file konfigurasi linker Kustom.
file. File konfigurasi linker kustom mungkin diperlukan untuk mempertahankan internal
atau private
anggota yang perlu dipertahankan.
Atribut Kustom
Saat rakitan ditautkan, jenis atribut kustom berikut akan dihapus dari semua anggota:
- System.ObsoleteAttribute
- System.MonoDocumentationNoteAttribute
- System.MonoExtensionAttribute
- System.MonoInternalNoteAttribute
- System.MonoLimitationAttribute
- System.MonoNotSupportedAttribute
- System.MonoTODOAttribute
- System.Xml.MonoFIXAttribute
Saat rakitan ditautkan, jenis atribut kustom berikut akan dihapus dari semua anggota dalam Build rilis:
- 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