逐步解說:系結Android Kotlin連結庫
重要
我們目前正在調查 Xamarin 平臺上的自定義系結使用方式。 請接受 這項調查 ,以通知未來的開發工作。
Xamarin 可讓行動開發人員使用 Visual Studio 和 C# 建立跨平臺原生行動應用程式。 您可以使用現成可用的 Android 平臺 SDK 元件,但在許多情況下,您也想要使用針對該平臺撰寫的第三方 SDK,而 Xamarin 可讓您透過系結來執行此動作。 若要將第三方 Android 架構併入 Xamarin.Android 應用程式,您必須先為其建立 Xamarin.Android 系結,才能在應用程式中使用它。
Android 平臺及其原生語言和工具不斷演進,包括最近推出的 Kotlin 語言,最終將取代 Java。 有一些 3D 派對 SDK 已經從 Java 遷移至 Kotlin,它讓我們面臨新的挑戰。 雖然 Kotlin 系結程式類似於 Java,但它需要額外的步驟和組態設定,才能成功建置和執行作為 Xamarin.Android 應用程式的一部分。
本文件的目標是概述解決此案例的高階方法,並提供詳細的逐步指南,並提供簡單的範例。
背景
Kotlin 於 2016 年 2 月發行,並在 2017 年將標準 Java 編譯程式定位為 Android Studio 的替代方案。 2019年晚些時候,谷歌宣佈,Kotlin 程式設計語言將成為Android應用程式開發人員慣用的語言。 高階系結方法類似於 一般 Java 連結庫 的系結程式,其中包含一些重要的 Kotlin 特定步驟。
必要條件
為了完成這個逐步解說,您需要:
建置原生連結庫
第一個步驟是使用Android Studio建置原生 Kotlin 連結庫。 連結庫通常是由第三方開發人員提供,或在Google的Maven存放庫和其他遠端存放庫取得。 例如,在本教學課程中,會建立泡泡選擇器 Kotlin 連結庫的系結:
從 GitHub 下載 連結庫的原始程式碼 ,並將它解壓縮到本機資料夾 Bubble-Picker。
啟動 Android Studio,然後選取 [ 開啟現有的 Android Studio 專案 ] 選單選項,選擇泡泡選擇器本機資料夾:
確認 Android Studio 是最新的,包括 Gradle。 原始程式碼可以在 Android Studio v3.5.3、Gradle v5.4.1 上成功建置。 如需如何將 Gradle 更新至最新 Gradle 版本的 指示,請參閱這裡。
確認已安裝必要的 Android SDK。 原始程式碼需要Android SDK v25。 開啟 [工具 > SDK 管理員 ] 選單選項以安裝 SDK 元件。
更新並同步處理位於項目資料夾根目錄的主要 build.gradle 組態檔:
將 Kotlin 版本設定為 1.3.10
buildscript { ext.kotlin_version = '1.3.10' }
註冊預設 Google 的 Maven 存放庫,以便解決支持連結庫相依性:
allprojects { repositories { jcenter() maven { url "https://maven.google.com" } } }
更新組態檔之後,它已同步,Gradle 會顯示 [ 立即 同步處理] 按鈕,按它並等候同步處理程式完成:
提示
Gradle 的相依性快取可能已損毀,這有時會在網路連線逾時之後發生。 重新下載相依性和同步專案(需要網路)。
提示
Gradle 建置程式 (精靈) 的狀態可能已損毀。 停止所有 Gradle 精靈可能會解決此問題。 停止 Gradle 建置程式(需要重新啟動)。 如果是損毀的 Gradle 進程,您也可以嘗試關閉 IDE,然後終止所有 Java 進程。
提示
您的專案可能使用第三方外掛程式,這與專案中的其他外掛程式或專案所要求的 Gradle 版本不相容。
在右側開啟 [Gradle] 功能表、流覽至泡泡器 [工作>] 功能表、按兩下建置工作來執行建置工作,並等候建置程式完成:
開啟根資料夾檔案瀏覽器並瀏覽至組建資料夾:Bubble-Picker - bubblepicker ->> build> - outputs -> aar,將 bubblepicker-release.aar 檔案儲存為 bubblepicker-v1.0.aar,此檔案將在稍後的系結程式中使用:
AAR 檔案是 Android 封存,其中包含 Android 使用此 SDK 執行應用程式所需的已編譯 Kotlin 原始碼和資產。
準備元數據
第二個步驟是準備元數據轉換檔案,Xamarin.Android 會使用此檔案來產生個別的 C# 類別。 Xamarin.Android 系結專案會探索指定 Android 封存中的所有原生類別和成員,然後產生具有適當元數據的 XML 檔案。 然後,手動建立的元數據轉換檔案會套用至先前產生的基準,以建立用來產生 C# 程式代碼的最終 XML 定義檔。
元數據會使用 XPath 語法,並且由系結產生器用來影響系結元件的建立。 Java 系 結元數據 一文提供有關轉換的詳細資訊,這些轉換可以套用:
建立空 的 Metadata.xml檔案:
<?xml version="1.0" encoding="UTF-8"?> <metadata> </metadata>
定義 xml 轉換:
原生 Kotlin 連結庫有兩個相依性,您不想公開至 C# 世界,定義兩個轉換以完全忽略它們。 重要地說,原生成員不會從產生的二進位檔中移除,只會產生 C# 類別。 Java Decompiler 可用來識別相依性。 執行此工具並開啟稍早建立的 AAR 檔案,因此會顯示 Android 封存的結構,以反映所有相依性、值、資源、指令清單和類別:
使用 XPath 指示來定義略過處理這些封裝的轉換:
<remove-node path="/api/package[starts-with(@name,'org.jbox2d')]" /> <remove-node path="/api/package[starts-with(@name,'org.slf4j')]" />
原生
BubblePicker
類別有兩種方法getBackgroundColor
,且setBackgroundColor
下列轉換會將它變更為 C#BackgroundColor
屬性:<attr path="/api/package[@name='com.igalata.bubblepicker.rendering']/class[@name='BubblePicker']/method[@name='getBackground' and count(parameter)=0]" name="propertyName">BackgroundColor</attr> <attr path="/api/package[@name='com.igalata.bubblepicker.rendering']/class[@name='BubblePicker']/method[@name='setBackground' and count(parameter)=1 and parameter[1][@type='int']]" name="propertyName">BackgroundColor</attr>
不帶正負號的類型
UInt, UShort, ULong, UByte
需要特殊處理。 對於這些類型,Kotlin 會自動變更方法名稱和參數類型,這會反映在產生的程式代碼中:public open fun fooUIntMethod(value: UInt) : String { return "fooUIntMethod${value}" }
此程式代碼會編譯成下列 Java 位元組程式代碼:
@NotNull public String fooUIntMethod-WZ4Q5Ns(int value) { return "fooUIntMethod" + UInt.toString-impl(value); }
此外,這類
UIntArray, UShortArray, ULongArray, UByteArray
相關類型也會受到 Kotlin 的影響。 方法名稱會變更為包含額外的後綴,而且參數會變更為相同類型已簽署版本的元素陣列。 在下列範例中,類型的UIntArray
參數會自動int[]
轉換成 ,而且方法名稱會從fooUIntArrayMethod
變更為fooUIntArrayMethod--ajY-9A
。 後者是由 Xamarin.Android 工具探索,併產生為有效的方法名稱:public open fun fooUIntArrayMethod(value: UIntArray) : String { return "fooUIntArrayMethod${value.size}" }
此程式代碼會編譯成下列 Java 位元組程式代碼:
@NotNull public String fooUIntArrayMethod--ajY-9A(@NotNull int[] value) { Intrinsics.checkParameterIsNotNull(value, "value"); return "fooUIntArrayMethod" + UIntArray.getSize-impl(value); }
為了提供有意義的名稱,可以將下列元數據新增至 Metadata.xml,這會將名稱更新回 Kotlin 程式代碼中原本定義的名稱:
<attr path="/api/package[@name='com.microsoft.simplekotlinlib']/class[@name='FooClass']/method[@name='fooUIntArrayMethod--ajY-9A']" name="managedName">fooUIntArrayMethod</attr>
在 BubblePicker 範例中,沒有任何成員使用不帶正負號的類型,因此不需要進行其他變更。
根據預設,具有泛型參數的 Kotlin 成員會轉換成 Java 的參數。
Lang.Object
類型。 例如,Kotlin 方法具有泛型參數 <T>:public open fun <T>fooGenericMethod(value: T) : String { return "fooGenericMethod${value}" }
產生 Xamarin.Android 系結之後,方法會公開至 C#,如下所示:
[Register ("fooGenericMethod", "(Ljava/lang/Object;)Ljava/lang/String;", "GetFooGenericMethod_Ljava_lang_Object_Handler")] [JavaTypeParameters (new string[] { "T" })] public virtual string FooGenericMethod (Java.Lang.Object value);
Xamarin.Android 系結不支援 Java 和 Kotlin 泛型,因此會建立一般化 C# 方法來存取泛型 API。 身為解決方法,您可以建立包裝函式 Kotlin 連結庫,並以強型別的方式公開必要的 API,而不需要泛型。 或者,您可以在 C# 端建立協助程式,以透過強型別 API 以相同方式解決問題。
提示
藉由轉換元數據,任何變更都可以套用至產生的系結。 系 結 Java 連結庫 一文會詳細說明如何產生及處理元數據。
建置系結連結庫
下一個步驟是使用 Visual Studio 系結範本建立 Xamarin.Android 系結專案、新增必要的元數據、原生參考,然後建置專案以產生消費性連結庫:
開啟 Visual Studio for Mac 並建立新的 Xamarin.Android 系結連結庫專案,併為其命名,在此案例 中測試BubblePicker.Binding 並完成精靈。 Xamarin.Android 系結樣本位於下列路徑:Android > 連結庫系結庫: >
在 [轉換] 資料夾中,有三個主要轉換檔案:
- Metadata.xml – 允許對最終 API 進行變更,例如變更產生的系結命名空間。
- EnumFields.xml – 包含 Java int 常數與 C# 列舉之間的對應。
- EnumMethods.xml – 允許將方法參數和從 Java int 常數傳回類型變更為 C# 列舉。
保留空白 EnumFields.xml 和 EnumMethods.xml 檔案,並更新 Metadata.xml 來定義您的轉換。
將現有的 Transformations/Metadata.xml 檔案取代為 在上一個步驟中建立的Metadata.xml 檔案。 在 [屬性] 視窗中,確認 [建置動作] 檔案已設定為 TransformationFile:
將 您在步驟 1 中建置的 bubblepicker-v1.0.aar 檔案新增至系結專案做為原生參考。 若要新增原生連結庫參考,請開啟 finder 並流覽至具有 Android 封存的資料夾。 將封存拖放到 方案總管 的 Jars 資料夾中。 或者,您可以使用 Jars 資料夾中的 [ 新增 內容] 選單選項,然後選擇 [ 現有檔案...]。 針對本逐步解說的目的,選擇將檔案複製到目錄。 請務必確認 [建置動作 ] 已設定為 LibraryProjectZip:
新增 Xamarin.Kotlin.StdLib NuGet 套件的參考。 此套件是 Kotlin 標準連結庫的系結。 如果沒有此套件,系結只有在 Kotlin 連結庫未使用任何 Kotlin 特定類型時才會運作,否則所有這些成員都不會公開給 C#,且任何嘗試取用系結的應用程式會在運行時間當機。
提示
由於 Xamarin.Android 的限制,每個系結專案只能新增單一 Android 封存 (AAR) 系結工具。 如果需要包含多個 AAR 檔案,則需要多個 Xamarin.Android 專案,每個 AAR 各一個。 如果這是本逐步解說的情況,則此步驟的前四個動作必須針對每個封存重複。 作為替代選項,您可以將多個 Android 封存手動合併為單一封存,因此您可以使用單一 Xamarin.Android 系結專案。
最後一個動作是建置連結庫,而且沒有任何編譯錯誤。 如果是編譯錯誤,您可以使用您稍早藉由新增 xml 轉換元數據來新增、移除或重新命名連結庫成員所建立的 Metadata.xml 檔案來處理它們。
取用系結連結庫
最後一個步驟是在 Xamarin.Android 應用程式中取用 Xamarin.Android 系結連結庫。 建立新的 Xamarin.Android 專案、新增系結連結庫的參考,並轉譯泡泡選擇器 UI:
建立 Xamarin.Android 專案。 使用 Android 應用程式 > Android > 應用程式作為起點,然後選取 [最新] 和 [最大] 作為 [目標平臺] 選項,以避免相容性問題。 下列所有步驟都會以此項目為目標:
將項目參考新增至系結專案,或新增先前建立之 DLL 的參考:
新增您稍早新增至 Xamarin.Kotlin.StdLib NuGet 套件的參考。 它會新增任何需要交出運行時間之 Kotlin 特定類型的支援。 如果沒有此套件,應用程式就可以編譯,但在運行時間會當機:
將
BubblePicker
控件新增至的MainActivity
Android 版面配置。 開啟 testBubblePicker/Resources/layout/content_main.xml 檔案,並將 BubblePicker 控件節點附加為根 RelativeLayout 控件的最後一個專案:<?xml version="1.0" encoding="utf-8"?> <RelativeLayout …> … <com.igalata.bubblepicker.rendering.BubblePicker android:id="@+id/picker" android:layout_width="match_parent" android:layout_height="match_parent" app:backgroundColor="@android:color/white" /> </RelativeLayout>
更新應用程式的原始程式碼,並將初始化邏輯新增至
MainActivity
,這會啟動泡泡選擇器 SDK:protected override void OnCreate(Bundle savedInstanceState) { ... var picker = FindViewById<BubblePicker>(Resource.Id.picker); picker.BubbleSize = 20; picker.Adapter = new BubblePickerAdapter(); picker.Listener = new BubblePickerListener(picker); ... }
BubblePickerAdapter
和BubblePickerListener
是從頭開始建立的兩個類別,可處理泡泡數據和控制互動:public class BubblePickerAdapter : Java.Lang.Object, IBubblePickerAdapter { private List<string> _bubbles = new List<string>(); public int TotalCount => _bubbles.Count; public BubblePickerAdapter() { for (int i = 0; i < 10; i++) { _bubbles.Add($"Item {i}"); } } public PickerItem GetItem(int itemIndex) { if (itemIndex < 0 || itemIndex >= _bubbles.Count) return null; var result = _bubbles[itemIndex]; var item = new PickerItem(result); return item; } } public class BubblePickerListener : Java.Lang.Object, IBubblePickerListener { public View Picker { get; } public BubblePickerListener(View picker) { Picker = picker; } public void OnBubbleDeselected(PickerItem item) { Snackbar.Make(Picker, $"Deselected: {item.Title}", Snackbar.LengthLong) .SetAction("Action", (Android.Views.View.IOnClickListener)null) .Show(); } public void OnBubbleSelected(PickerItem item) { Snackbar.Make(Picker, $"Selected: {item.Title}", Snackbar.LengthLong) .SetAction("Action", (Android.Views.View.IOnClickListener)null) .Show(); } }
執行應用程式,其應該會轉譯泡泡選擇器 UI:
此範例需要額外的程式代碼來呈現元素樣式和處理互動,但
BubblePicker
控件已成功建立並啟用。
恭喜! 您已成功建立 Xamarin.Android 應用程式和系結連結庫,這會取用 Kotlin 連結庫。
您現在應該有基本的 Xamarin.Android 應用程式,可透過 Xamarin.Android 系結連結庫使用原生 Kotlin 連結庫。 本逐步解說會刻意使用基本範例來進一步強調所引進的重要概念。 在真實世界的案例中,您可能需要公開更多 API,並將元數據轉換套用至它們。