8.在XamarinAndroid上進一步控制包的大小
在Android上鏈接
Xamarin.Android應用程序使用鏈接器來減小應用程序的大小。鏈接器使用應用程序的靜態分析來確定哪些程序集、類型、成員被實際使用。鏈接器的行為就像一個GC,不斷尋找被引用的程序集,類型和成員,直到整個引用的程序集,類型和成員都被找到。沒被引用到的類型和程序集都被拋棄掉。
例如,Hello, Android 示例:
組態 | 1.2.0大小 | 4.0.1大小 |
無鏈接發布: | 14.0 MB | 16.0 MB |
通過鏈接發布: | 4.2 MB | 2.9 MB |
通過鏈接器處理後的尺寸在1.2.0版本中相當於原來的30%,在4.0.1版本中就只相當於原來的18%。效果非常好啊
控制
鏈接器基於靜態分析。因此,依賴運行時環境的代碼都不會被檢測到(比如反射,動態生成對象等等都無法被鏈接器檢測到):
// 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)); } }
鏈接器行為
控制鏈接器的主要機制是“ 項目選項”對話框中的鏈接器行為(在Visual Studio中)下拉列表。有三個選項:
- 不要鏈接(在Visual Studio中
- 鏈接SDK程序集(僅限Sdk程序集)
- 鏈接所有程序集(Sdk和用戶程序集)
在沒有鏈接選項將禁用鏈接器; 上面的“無鏈接發布”應用程序大小就是這樣的。這種方法對於解決運行時故障很有用,以查看鏈接器是否做得對。通常不建議在生產版本中使用此設置。
該鏈接SDK組件選項僅鏈接 來與Xamarin.Android組件。所有其他程序集(如您的代碼)不鏈接。
該鏈接的所有組件的選擇鏈路上的所有組件,這意味著你的代碼如果沒有被靜態引用則會被刪除。
上面的示例代碼可以成功使用“不鏈接和鏈接SDK程序集”鏈接選項。但選擇“ 鏈接所有程序集”選項將會失敗,並生成以下錯誤:
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)
保留代碼
鏈接器有時會刪除掉你本來要保留的代碼。例如:
-
您可能有通過動態反射調用的代碼
System.Reflection.MemberInfo.Invoke
。 -
如果動態實例化類型,則可能需要保留類型的默認構造函數。
-
如果使用XML序列化,則可能需要保留類型的屬性。
在這些情況下,您可以使用 Android.Runtime.Preserve 屬性用來標記沒有被靜態引用,但是你的應用程序仍然所需要的成員。您可以將此屬性應用於類型的每個成員或類型本身。
在下面的例子中,這個屬性用來保存Example
類的構造函數:
public class Example { [Android.Runtime.Preserve] public Example () { } }
如果要保留整個類型,則可以使用以下屬性語法:
[Android.Runtime.Preserve (AllMembers = true)]
例如,在下面的代碼片段中,整個Example
類都被保留用於XML序列化:
[Android.Runtime.Preserve (AllMembers = true)] class Example { // Compiler provides default constructor... }
有時候你想保留某些成員,但是只有保留了包含的類型。在這些情況下,請使用以下屬性語法:
[Android.Runtime.Preserve (Conditional = true)]
如果您不想依賴Xamarin庫(例如,您正在構建一個跨平臺的可移植類庫(PCL)),你仍然可以使用該Android.Runtime.Preserve
屬性。為此,請在Android.Runtime
名稱空間內聲明一個PreserveAttribute類, 如下所示:
namespace Android.Runtime { public sealed class PreserveAttribute : System.Attribute { public bool AllMembers; public bool Conditional; } }
錯誤標識
如果不能使用[Preserve]屬性,通常需要提供一段預處理指令,以便鏈接器相信這個類型被使用並保留這個代碼塊。要使用這個技術我們可以這樣做:
[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
可以指定一組用戶提供的程序集不被鏈接處理(以保留整個程序集),同時允許使用AndroidLinkSkip MSBuild屬性通過鏈接SDK程序集行為跳過其他用戶程序集:
<PropertyGroup> <AndroidLinkSkip>Assembly1;Assembly2</AndroidLinkSkip> </PropertyGroup>
鏈接說明
該生成操作可在其中可以包含一個文件中使用 自定義連接配置文件定義需要保留的私有或者內部成員。
自定義屬性
鏈接程序集時,將從所有成員中刪除以下自定義屬性類型:
- System.ObsoleteAttribute
- System.MonoDocumentationNoteAttribute
- System.MonoExtensionAttribute
- System.MonoInternalNoteAttribute
- System.MonoLimitationAttribute
- System.MonoNotSupportedAttribute
- System.MonoTODOAttribute
- System.Xml.MonoFIXAttribute
鏈接程序集時,下列自定義屬性類型將從發布版本中的所有成員中刪除:
- 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
8.在XamarinAndroid上進一步控制包的大小