Java Bindings Metadata 的原文翻译

原文:https://developer.xamarin.com/guides/android/advanced_topics/binding-a-java-library/customizing-bindings/java-bindings-metadata/

 

Xamarin.Android的c#代码通过绑定调用Java库,这是一种机制,抽象的底层细节中指定 Java Native Interface(JNI)。Xamarin.Android提供了一个工具生成这些绑定。

这个工具允许开发人员使用Metadata 控制如何创建绑定,它允许程序修改命名空间和重命名成员。本文讨论Metadata是如何工作的,简述Metadata支持的 属性,

并解释如何通过修改此Metadata解决绑定问题。

概述:

Xamarin.Android Java绑定库在现有Android库的帮助下绑定生成器工具试图自动化所需的大部分工作。 当绑定一个Java库,Xamarin.Android将检查Java类和生成的列表所有的包、类型和成员被绑定。 这个api列表存储在一个XML文件,在这里:项目路径\obj\Debug\api.xml 和 项目路径\obj\Release\api.xml

绑定生成器将使用api.xml文件作为指导原则生成必要的c#包装类。这个XML文件的内容 是谷歌的Android开源项目的一种变体格式。下面的代码片段是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包中命名继承java.lang.object的Manifest。 在许多情况下,人们希望Java API更像”.NET“或纠正从编译生成绑定的问题。

例如,可能需要改变Java包名到.Net命名空间、重命名一个类,或者改变方法的返回类型。 这些修改并不是通过直接修改api.xml。相反修改通过特殊的Java绑定库模板XML文件。 当编译Xamarin.Android绑定assembly,绑定生成器将受到这些映射文件的影响。

 

在创建绑定assembly时这些XML映射文件可以在项目的Transforms目录下找到:

     MetaData.xml – 允许更改最后生成的API,如改变绑定生成的命名空间。

     EnumFields.xml – 包含在Java常量和C#枚举间映射。

     EnumMethods.xml – 允许改变方法参数和返回值类型从Java int型常量到C#枚举。

 

MetaData.xml 是这些文件中最重要的,因为它允许对绑定的一般更改,如:

重命名命名空间、类、方法或字段以遵从.Net规则。

删除没有必要的命名空间、类、方法或字段。

移动类到不同的命名空间。

添加额外支持类使设计遵从.NET framework模式。

让我们继续讨论 Metadata.xml 的更多细节

Metadata.xml 文件使用绑定生成器来影响绑定的assembly创建。 Metadata格式使用XPath语法和几乎以GAPI Metadata指南中相同的GAPI Metadata描述。该文件是一个 强大的基于XPath的机制改变、添加、隐藏,或在API文件中移到任何元素或属性。Metadata规范所有的规则元素 包括路径属性来确定节点被应用的规则。

按照以下顺序应用规则:

add-node – 通过路径属性追加一个子节点到指定节点。

attr – 通过路径属性设置指定元素属性的值。

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 Xamarin 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 elements的一部分:

Common Path             Description

interface               用于定位一个 Java 接口. e.g. /interface[@name='AuthListener'].

class                     用于定位一个类 . e.g. /class[@name='MapView'].

method                用于定位一个类或接口中的方法 . e.g. /class[@name='MapView']/method[@name='setTitleSource'].

parameter            确定方法的一个参数. e.g. /parameter[@name='p0']

 

增加类型:

添加一个节点将告诉 Xamarin.Android 绑定项目到一个 wrapper class 到 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>

删除类型:

它可以指导Xamarin.Android绑定生成器忽略一个Java类型和不绑定它。 这是通过添加删除节点XML元素到metadata.xml文件:

<remove-node path="/api/package[@name='{package_name}']/class[@name='{name}']" />

重命名成员:

重命名成员不能通过直接编辑api.xml文件,因为Xamarin.Android需要原始的Java Native Interface(JNI)的名字。 因此,  //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 { ... }

注意包装类已经被重命名为新名称,而原始的Java类型仍Manifest。它不再是可能的Xamarin.Android绑定类访问android.Manifest上的任何方法。

包装类绑定到一个不存在的Java类型。

改变包装类型的managed名称(或方法),必须设置managedName属性,如本例所示:

<attr path="/api/package[@name='android']/class[@name='Manifest']"
    name="managedName">NewName</attr>

重命名EventArg包装类

当Xamarin.Android 绑定生成器确定一个onXXX setter method 为一个侦听器类型,一个c#事件和EventArgs子类将基于java的监听器模式生成一个.NET 风格 API

例如参考以下Java类和方法:

com.someapp.android.mpa.guidance.NavigationManager.on2DSignNextManuever(NextManueverListener listener);

Xamarin.Android 将前缀 on from the setter method and instead use 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.

argTypes

这个属性是放在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>
The next example illustrates an XPath expression for renaming the method java.lang.object.toString to Java.Lang.Object.NewManagedName:
<attr path="/api/package[@name='java.lang']/class[@name='Object']/method[@name='toString']"
    name="managedName">NewMethodName</attr>

managedType

用于改变一个方法的返回值类型。在某些情况下绑定生成器将错误地推断Java方法的返回类型,这将导致编译时错误。

在这种情况下,一个可能的解决办法是改变方法的返回类型。

例如,绑定生成器认为Java方法 de.neom.neoreadersdk.resolution.compareTo() 将返回int类型,这将导致错误消息。

Error CS0535: `DE.Neom.Neoreadersdk.Resolution' does not implement interface member `Java.Lang.IComparable.CompareTo(Java.Lang.Object)'.

下面的代码片段演示了如何改变生成c#方法的返回类型从int到一个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签名)

在接下来的例子中,附加方法的返回类型由SpannableStringBuilder改为IAppendable(回想一下,c#不支持covariant返回类型):

<attr path="/api/package[@name='android.text']/
    class[@name='SpannableStringBuilder']/
    method[@name='append']"
    name="managedReturn">Java.Lang.IAppendable</attr>

obfuscated

混淆类工具Java库可能会干扰Xamarin.Android绑定生成器生成c#包装类和它的能力。混淆类的特征包括:

类名包含 a $, i.e. a$.class

类名是小写字符的完全损坏, i.e. a.class

这段代码是如何生成的一个"un-obfuscated" C#类型的例子 :

<attr path="/api/package[@name='{package_name}']/class[@name='{name}']"
    name="obfuscated">false</attr>

propertyName

这个属性可以用来改变managed property的名称。使用propertyName的特殊例子涉及一个Java类的情况只有一个字段的getter方法。

在这种情况下绑定生成器想创建一个只写属性,在.net中是让人泄气的。以下代码片段显示了如何“remove”.NET属性通过设置propertyName为空字符串:

<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方法仍将由绑定生成器创建。

 

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>

visibility

这个属性是用来改变类、方法或property的可见性。例如,可能有必要调整protected型Java方法为相应的c# wrapper 是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>

EnumFields.xml and EnumMethods.xml

有情况下Android库使用整形常量来表示状态传递给属性或库的方法。 在许多情况下,绑定这些整形常量到c#枚举是有用的。为了生成这种映射,使用EnumFields.xml和EnumMethods.xml文件在绑定项目中。

用 EnumFields.xml 定义枚举

EnumFields.xml文件包含在Java 整形常量和 C# 枚举间映射。让我们看下面用c# enum创建一组整数常量的例子:

<mapping jni-class="com/skobbler/ngx/map/realreach/SKRealReachSettings" clr-enum-type="Skobbler.Ngx.Map.RealReach.SKMeasurementUnit">
    <field jni-name="UNIT_SECOND" clr-name="Second" value="0" />
    <field jni-name="UNIT_METER" clr-name="Meter" value="1" />
    <field jni-name="UNIT_MILIWATT_HOURS" clr-name="MilliwattHour" value="2" />
</mapping>

这里我们在命名空间Skobbler.Ngx.Map.RealReach中定义一个Java 类 SKRealReachSettings 和一个叫做SKRealReachSettings的C#枚举。

字段定义Java常数的名称(例如UNIT_SECOND),enum条目的名称(Second),和两个实体所表示的整数值(0)。

 

用 EnumMethods.xml 文件定义Get/Set方法

EnumMethods.xml文件允许改变方法参数和返回类型从Java整形常量到c#枚举。

换句话说,它将映射c#枚举的读和写(EnumFields.xml文件中定义)到Java 整形常量的get和set方法。

鉴于上述SKRealReachSettings枚举定义,以下EnumMethods.xml文件将定义这个枚举的get/set访问器:

<mapping jni-class="com/skobbler/ngx/map/realreach/SKRealReachSettings">
    <method jni-name="getMeasurementUnit" parameter="return" clr-enum-type="Skobbler.Ngx.Map.RealReach.SKMeasurementUnit" />
    <method jni-name="setMeasurementUnit" parameter="measurementUnit" clr-enum-type="Skobbler.Ngx.Map.RealReach.SKMeasurementUnit" />
</mapping>

第一个method将Java getMeasurementUnit方法的返回值映射到SKRealReachSettings枚举。

第二个method将setMeasurementUnit第一个参数映射到相同枚举。

所有的这些变化,你可以在Xamarin.Android使用下面代码设置MeasurementUnit:

realReachSettings.MeasurementUnit = SKMeasurementUnit.Second;

总结

本文讨论了如何Xamarin.Android使用metadata将Google AOSP格式变换为API定义的格式。

覆盖变化可以使用metadata.xml,它检查当重命名成员遇到局限性时,提出了支持xml属性的列表的各个属性被应用时的描述。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值