[Xamarin]關於Linker設定
不論是在Android或iOS的專案中,專案屬性內都有一個Linker選項,例如下圖:
Linker設定指的就是程式碼的最佳化,
所謂的最佳化就是在編譯程式碼時僅編譯出有使用到的物件、方法、屬性及事件等,
每個選項的編譯行為如下:
None 或Dont link:不進行任何的最佳化,通常在需要進行DeBug時或特殊需求才會選此項。
SDK Assemblies only:對Xamarin.Android或Xamarin.iOS的做最佳化
All Assemblies:最所有元件最佳化。
預設在DeBug模式下使用None,
Release模式下預設是SDK Assemblies only,
以Android為例,從編譯出的apk檔的大小可以看出很明顯的差異。
使用SDK Assemblies only編譯出的大小約3.4MB:
因此在撰寫程式碼時, 應儘量避免使用到反射的寫法,
如下程式碼使用了反射的方式更改了Button的文字,使用None模式時一切正常:
typeof(Button).GetProperty("Text").SetValue(button, "test Linker");
但改成SDK Assemblies only後,執行立刻就報錯了
錯誤為NullReferenceException,因為GetProperty("Text")找不到名稱為Text的屬性回傳了Null。
那怎樣的情況這個屬性才會被編譯?需要出現如下程式碼片段:
雖然上面的程式碼會讓Text屬性被編譯進去,
但也只編譯了Set的部分,如果用反射去Get照樣會死給你看...
但如果還是得需要用到反射,
我們可以自行定義XML檔案指定編譯時省略哪些東西不做最佳化處理。
以上方的程式碼來說,我們希望Button的Text不要被最佳化,
而Text屬性又是繼承了TextView而來:
所以這裡要處理的其實是Android.Widget.TextView,
而由上方的組件資訊可得知元件名稱
接著就可以在專案中加入Xml,並撰寫以下內容:
<assembly fullname="Mono.Android">
<type fullname="Android.Widget.TextView" ></type>
</assembly>
</linker>
assembly fullname放的就是組件名稱,
type fullname裡放的就是要省略最佳化類別名(要含NameSpace)。
接著在Android中,選擇該Xml檔案,
在建置動作的地方選擇LinkDescription,並且重新建置專案。
如果是在iOS下,依照同樣的方法取得組件名稱與類別完整名稱後,
一樣建立Xml在專案目錄下並撰寫相同格式內容XML,
最後在專案屬性內iOS Build→於Additional mtouch arguments輸入(linker為自建目錄):
--xml=${ProjectDir}/linker/setting.xml
這樣就可以避免程式最佳化導致反射無法使用,
但相對的會增加一點點編譯出的程式大小(可能1~2K之類的)。
其他當然還是有方法可以避免被最佳化,
但要調整SDK的最佳化只能用此方式喔。
參考網頁:
Xamarin iOS-Linker
Xmarin Android-Linker
客製化Linker