Java 绑定元数据

适用于 Android Java 绑定库 的 .NET 会尝试通过工具(有时称为 绑定生成器)自动完成绑定现有 Android 库所需的大部分工作。 绑定 Java 库时,适用于 Android 的 .NET 将检查 Java 类,并生成要绑定的所有包、类型和成员的列表。 此 API 列表存储在可在 {project directory}\obj{Configuration}\api.xml 的 XML 文件中。

obj/Debug 文件夹中的 api.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 在扩展 android 的 Manifest 包中声明一个 java.lang.Object 类。

在许多情况下,需要人工协助使 Java API 感觉更像 .NET 或修正阻止绑定程序集编译的问题。 例如,可能需要将 Java 包名称更改为 .NET 命名空间、重命名类或更改方法的返回类型。

不应通过直接修改 api.xml 来实现这些更改。 而是记录更动在由 Java 绑定库模板提供的特殊 XML 文件中。 编译适用于 Android 绑定程序集的 .NET 时,在创建绑定程序集时,绑定生成器将受到这些映射文件的影响

Metadata.xml文件是其中最重要的文件,因为它允许对绑定进行常规用途更改,例如:

  • 重命名命名空间、类、方法或字段,使其遵循 .NET 约定。

  • 删除不需要的命名空间、类、方法或字段。

  • 将类移到不同的命名空间。

  • 添加其他支持类,使绑定的设计遵循 .NET 框架模式。

Metadata.xml 转换文件

正如我们了解到的,绑定生成器使用文件 Metadata.xml 来影响绑定程序集的创建。 元数据格式使用 XPath 语法。

此实现几乎是 XPath 1.0 的完整实现,因此支持 1.0 标准版中的项。 此文件是一种功能强大的基于 XPath 的机制,可用于更改、添加、隐藏或移动 API 文件中的任何元素或属性。 元数据规格中的所有规则元素都包含一个 path 属性,用于标识要向其应用规则的节点(s)。 以下是可用的元素类型:

  • 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-nodeMetadata.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);

.NET for Android 将从 setter 方法中删除前缀 on,并将 2DSignNextManuever 用作 EventArgs 子类名称的基础。 子类名称如下所示:

NavigationManager.2DSignNextManueverEventArgs

这不是合法的 C# 类名。 若要更正此问题,绑定作者必须使用 argsType 属性,并为 EventArgs 子类提供有效的 C# 名称:

<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 方法上,用于命名将为支持 Java 侦听器而生成的 EventArg 子类。 本指南的重命名 EventArg 包装器类一节中有更详细的介绍。

事件名称

指定事件名称。 如果该名称为空,它将阻止事件生成。 在重命名 EventArg 包装器类部分中对此进行了更详细的描述。

managedName

此属性用于更改包、类、方法或参数的名称。 例如,将 Java 类的名称从 MyClass 更改为 NewClassName:

<attr path="/api/package[@name='com.my.application']/class[@name='MyClass']" 
    name="managedName">NewClassName</attr>

下一个示例演示了如何使用 XPath 表达式将方法 java.lang.object.toString 重命名为 Java.Lang.Object.NewManagedName:

<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 的情况之一是针对某个 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>

请注意,Setter 和 getter 方法仍由绑定生成器创建,它们不会转换为 .NET 属性。

发送方

当方法映射到事件时,指定方法的哪个参数应为 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>