[Xamarin]關於Linker設定

[Xamarin]關於Linker設定

不論是在Android或iOS的專案中,專案屬性內都有一個Linker選項,例如下圖:

Android: 
image 

iOS:
image

 

 

 

Linker設定指的就是程式碼的最佳化,
所謂的最佳化就是在編譯程式碼時僅編譯出有使用到的物件、方法、屬性及事件等,
每個選項的編譯行為如下:

None 或Dont link:不進行任何的最佳化,通常在需要進行DeBug時或特殊需求才會選此項。

SDK Assemblies only:對Xamarin.Android或Xamarin.iOS的做最佳化

All Assemblies:最所有元件最佳化。

 

預設在DeBug模式下使用None,
Release模式下預設是SDK Assemblies only
以Android為例,從編譯出的apk檔的大小可以看出很明顯的差異。

使用None編譯出的大小約26MB:
image

使用SDK Assemblies only編譯出的大小約3.4MB:
image

因此在撰寫程式碼時, 應儘量避免使用到反射的寫法,
如下程式碼使用了反射的方式更改了Button的文字,使用None模式時一切正常:


            typeof(Button).GetProperty("Text").SetValue(button, "test Linker");


image

 

 

但改成SDK Assemblies only後,執行立刻就報錯了
image

錯誤為NullReferenceException,因為GetProperty("Text")找不到名稱為Text的屬性回傳了Null。
那怎樣的情況這個屬性才會被編譯?需要出現如下程式碼片段:


 

 

雖然上面的程式碼會讓Text屬性被編譯進去,
但也只編譯了Set的部分,如果用反射去Get照樣會死給你看...

image

 

但如果還是得需要用到反射,
我們可以自行定義XML檔案指定編譯時省略哪些東西不做最佳化處理。
以上方的程式碼來說,我們希望Button的Text不要被最佳化,
而Text屬性又是繼承了TextView而來:

image
image

 

所以這裡要處理的其實是Android.Widget.TextView
而由上方的組件資訊可得知元件名稱
image

接著就可以在專案中加入Xml,並撰寫以下內容:


  <assembly fullname="Mono.Android">
    <type fullname="Android.Widget.TextView" ></type>
  </assembly>
</linker>

 

assembly fullname放的就是組件名稱,
type fullname裡放的就是要省略最佳化類別名(要含NameSpace)。

接著在Android中,選擇該Xml檔案,
在建置動作的地方選擇LinkDescription,並且重新建置專案。
image

 

如果是在iOS下,依照同樣的方法取得組件名稱與類別完整名稱後,
一樣建立Xml在專案目錄下並撰寫相同格式內容XML,
最後在專案屬性內iOS Build→於Additional mtouch arguments輸入(linker為自建目錄):

 

--xml=${ProjectDir}/linker/setting.xml

 

如圖
image

這樣就可以避免程式最佳化導致反射無法使用,
但相對的會增加一點點編譯出的程式大小(可能1~2K之類的)。

其他當然還是有方法可以避免被最佳化,
但要調整SDK的最佳化只能用此方式喔。


參考網頁:
Xamarin iOS-Linker
Xmarin Android-Linker
客製化Linker

範例程式碼下載(Android+iOS)