Java 系結元數據
適用於 Android Java 系結連結庫的 .NET 會嘗試自動化系結現有 Android 連結庫 所需的大部分工作,有時稱為 系結產生器的工具。 系結 Java 連結庫時,適用於 Android 的 .NET 會檢查 Java 類別,並產生要系結之所有套件、類型和成員的清單。 此 API 清單儲存在可在 {project directory}\obj{Configuration}\api.xml 的 XML 檔案中。
系結產生器會使用 api.xml 檔案作為產生必要 C# 包裝函式類別的指導方針。 下列代碼段是api.xml內容的範例:
<api>
<package name="android">
<class abstract="false" deprecated="not deprecated" extends="java.lang.Object"
extends-generic-aware="java.lang.Object"
final="true"
name="Manifest"
static="false"
visibility="public">
<constructor deprecated="not deprecated" final="false"
name="Manifest" static="false" type="android.Manifest"
visibility="public">
</constructor>
</class>
...
</api>
在這裡範例中,api.xml在名為 Manifest
的封裝中android
宣告類別,以擴充 java.lang.Object
。
在許多情況下,需要人為協助,讓 Java API 感覺更像是 .NET,或修正導致系結元件無法編譯的問題。 例如,可能需要將 Java 套件名稱變更為 .NET 命名空間、重新命名類別,或變更方法的傳回類型。
直接修改 api.xml ,就不應達成這些變更。 相反地,變更會記錄在 Java 系結連結庫範本所提供的特殊 XML 檔案中。 編譯 .NET for Android 系結元件時,建立系結元件時,系結產生器將受到這些對應檔的影響
Metadata.xml檔案是其中最重要的檔案,因為它允許對系結進行一般用途的變更,例如:
重新命名命名空間、類別、方法或字段,使其遵循 .NET 慣例。
拿掉不需要的命名空間、類別、方法或欄位。
將類別移至不同的命名空間。
新增其他支持類別,讓系結的設計遵循 .NET Framework 模式。
Metadata.xml轉換檔案
如我們所瞭解,系結產生器會使用Metadata.xml檔案來影響系結元件的建立。 元數據格式使用 XPath 語法。
此實作幾乎是 XPath 1.0 的完整實作,因此支援 1.0 標準中的專案。 此檔案是以強大的 XPath 為基礎的機制,可用來變更、新增、隱藏或移動 API 檔案中的任何項目或屬性。 元數據規格中的所有規則元素都包含 path
屬性,以識別要套用規則的節點。 以下是可用的項目類型:
- add-node – 將子節點附加至 path 屬性所指定的節點。
- attr – 設定 path 屬性所指定專案之屬性值。
- remove-node – 移除符合指定 XPath 的節點。
以下是Metadata.xml檔案的範例:
<metadata>
<!-- Normalize the namespace for .NET -->
<attr path="/api/package[@name='com.evernote.android.job']"
name="managedName">Evernote.AndroidJob</attr>
<!-- Don't need these packages for the .NET for Android binding/public API -->
<remove-node path="/api/package[@name='com.evernote.android.job.v14']" />
<remove-node path="/api/package[@name='com.evernote.android.job.v21']" />
<!-- Change a parameter name from the generic p0 to a more meaningful one. -->
<attr path="/api/package[@name='com.evernote.android.job']/class[@name='JobManager']/method[@name='forceApi']/parameter[@name='p0']"
name="name">api</attr>
</metadata>
下列列出 Java API 的一些較常用 XPath 元素:
interface
– 用來尋找 Java 介面。 例如/interface[@name='AuthListener']
。class
– 用來尋找類別 。 例如/class[@name='MapView']
。method
– 用來在 Java 類別或介面上尋找方法。 例如/class[@name='MapView']/method[@name='setTitleSource']
。parameter
– 識別方法的參數。 例如,/parameter[@name='p0']
新增類型
元素 add-node
會告訴 .NET for Android 系結專案將新的類別新增至 api.xml。 例如,下列代碼段會指示系結產生器建立具有建構函式和單一字段的類別:
<add-node path="/api/package[@name='org.alljoyn.bus']">
<class abstract="false" deprecated="not deprecated" final="false" name="AuthListener.AuthRequest" static="true" visibility="public" extends="java.lang.Object">
<constructor deprecated="not deprecated" final="false" name="AuthListener.AuthRequest" static="false" type="org.alljoyn.bus.AuthListener.AuthRequest" visibility="public" />
<field name="p0" type="org.alljoyn.bus.AuthListener.Credentials" />
</class>
</add-node>
拿掉類型
可以指示適用於Android系結產生器的 .NET 忽略 Java 類型,而不是系結它。 這可藉由將 XML 元素新增 remove-node
至 Metadata.xml 檔案來完成:
<remove-node path="/api/package[@name='{package_name}']/class[@name='{name}']" />
重新命名成員
無法直接編輯 api.xml 檔案來重新命名成員,因為適用於 Android 的 .NET 需要原始的 Java 原生介面 (JNI) 名稱才能與 Java 交談。 因此, //class/@name
無法改變屬性;如果是,系結將無法運作。
請考慮我們想要將類型重新命名為 的情況。 android.Manifest
若要達成此目的,我們可能會嘗試直接編輯 api.xml 並重新命名 類別,如下所示:
<attr path="/api/package[@name='android']/class[@name='Manifest']"
name="name">NewName</attr>
這會導致系結產生器為包裝函式類別建立下列 C# 程式代碼:
[Register ("android/NewName")]
public class NewName : Java.Lang.Object { ... }
請注意,包裝函式類別已重新命名為 NewName
,而原始 Java 類型仍然是 Manifest
。 .NET for Android 系結類別無法存取上 android.Manifest
的任何方法;包裝函式類別系結至不存在的 Java 類型。
若要正確變更包裝類型(或 方法)的「Managed」名稱,您必須設定 managedName
屬性,如下列範例所示:
<attr path="/api/package[@name='android']/class[@name='Manifest']"
name="managedName">NewName</attr>
managedName
嘗試重新命名任何成員時,需要使用 ,例如類別、介面、方法和參數。
重新命名 EventArg
包裝函式類別
當 .NET for Android 系結產生器識別onXXX
接聽程式類型的 setter 方法時,會產生 C# 事件和EventArgs
子類別,以支援 Java 型接聽程式模式的 .NET 口味 API。 例如,請考慮下列 Java 類別和方法:
com.someapp.android.mpa.guidance.NavigationManager.on2DSignNextManuever(NextManueverListener listener);
適用於 Android 的 .NET 會從 setter 方法卸除前置 on
詞,並改為使用 2DSignNextManuever
作為子類別名稱的基礎 EventArgs
。 子類別會命名如下:
NavigationManager.2DSignNextManueverEventArgs
這不是合法的 C# 類別名稱。 若要更正此問題,系結作者必須使用 argsType
屬性,併為子類別提供有效的 C# 名稱 EventArgs
:
<attr path="/api/package[@name='com.someapp.android.mpa.guidance']/
interface[@name='NavigationManager.Listener']/
method[@name='on2DSignNextManeuver']"
name="argsType">NavigationManager.TwoDSignNextManueverEventArgs</attr>
支援的屬性
下列各節說明轉換 Java API 的一些屬性。
argsType
這個屬性會放在 setter 方法上,以命名 EventArg
將產生以支援 Java 接聽程式之子類別。 本指南將詳細說明重新命名 EventArg 包裝函式類別一節。
eventName
指定事件的名稱。 如果名稱是空的,則會防止產生事件。 在重新命名 EventArg 包裝函式類別一節中會詳細說明這一點。
managedName
這可用來變更封裝、類別、方法或參數的名稱。 例如,將 Java 類別 MyClass
的名稱變更為 NewClassName
:
<attr path="/api/package[@name='com.my.application']/class[@name='MyClass']"
name="managedName">NewClassName</attr>
下一個範例說明將 方法 java.lang.object.toString
重新命名為 Java.Lang.Object.NewManagedName
的 XPath 運算式:
<attr path="/api/package[@name='java.lang']/class[@name='Object']/method[@name='toString']"
name="managedName">NewMethodName</attr>
managedType
managedType
是用來變更方法的傳回型別。 在某些情況下,系結產生器會錯誤地推斷 Java 方法的傳回類型,這會導致編譯時間錯誤。 在此情況下,其中一個可能的解決方案是變更 方法的傳回類型。
例如,系結產生器認為 Java 方法 de.neom.neoreadersdk.resolution.compareTo()
應該傳回 int
,並採用 Object
做為參數,這會導致錯誤訊息 錯誤 CS0535:『DE。Neom.Neoreadersdk.Resolution' 不會實作介面成員 'Java.Lang.IComparable.CompareTo(Java.Lang.Object)'。
下列代碼段示範如何將產生的 C# 方法的第一個參數類型從 DE.Neom.Neoreadersdk.Resolution
變更為 Java.Lang.Object
:
<attr path="/api/package[@name='de.neom.neoreadersdk']/
class[@name='Resolution']/
method[@name='compareTo' and count(parameter)=1 and
parameter[1][@type='de.neom.neoreadersdk.Resolution']]/
parameter[1]" name="managedType">Java.Lang.Object</attr>
managedReturn
變更方法的傳回型別。 這不會變更傳回屬性(因為傳回屬性的變更可能會導致 JNI 簽章的不相容變更)。 在下列範例中,方法的 append
傳回型別會從 SpannableStringBuilder
變更為 IAppendable
:
<attr path="/api/package[@name='android.text']/
class[@name='SpannableStringBuilder']/
method[@name='append']"
name="managedReturn">Java.Lang.IAppendable</attr>
混淆
混淆 Java 連結庫的工具可能會干擾 .NET for Android 系結產生器及其產生 C# 包裝函式類別的能力。 模糊類別的特性包括:
- 類別名稱包含 $,也就是 a$.class
- 類別名稱完全遭到小寫字元入侵,亦即 a.class
此代碼段是如何產生「未混淆」C# 類型的範例:
<attr path="/api/package[@name='{package_name}']/class[@name='{name}']"
name="obfuscated">false</attr>
propertyName
這個屬性可用來變更 Managed 屬性的名稱。
使用 propertyName
的特殊案例牽涉到 Java 類別只有欄位的 setter 方法的情況。 在此情況下,系結產生器會想要建立僅限寫入的屬性,這是 .NET 中不建議使用的屬性。 下列代碼段示範如何將 設定 propertyName
為空字串,以「移除」.NET 屬性:
<attr path="/api/package[@name='org.java_websocket.handshake']/class[@name='HandshakeImpl1Client']/method[@name='setResourceDescriptor'
and count(parameter)=1
and parameter[1][@type='java.lang.String']]"
name="propertyName"></attr>
<attr path="/api/package[@name='org.java_websocket.handshake']/class[@name='HandshakeImpl1Client']/method[@name='getResourceDescriptor'
and count(parameter)=0]"
name="propertyName"></attr>
請注意,Bindings Generator 仍然會建立 setter 和 getter 方法,它們只會不會轉換成 .NET 屬性。
sender
指定當方法對應至事件時,方法的哪一個參數應該是 sender
參數。 值可以為 true
或 false
。 例如:
<attr path="/api/package[@name='android.app']/
interface[@name='TimePickerDialog.OnTimeSetListener']/
method[@name='onTimeSet']/
parameter[@name='view']"
name="sender">true</ attr>
可視性
這個屬性可用來變更類別、方法或屬性的可見性。 例如,可能需要升級 protected
Java 方法,使其對應的 C# 包裝函式為 public
:
<!-- Change the visibility of a class -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']" name="visibility">public</attr>
<!-- Change the visibility of a method -->
<attr path="/api/package[@name='namespace']/class[@name='ClassName']/method[@name='MethodName']" name="visibility">public</attr>