Metadata vazeb Java

Důležité

V současné době prošetřujeme využití vlastních vazeb na platformě Xamarin. Pokud chcete informovat budoucí úsilí o rozvoj, využijte tento průzkum .

Kód jazyka C# v Xamarin.Android volá knihovny Jazyka Java prostřednictvím vazeb, což je mechanismus, který abstrahuje podrobnosti nízké úrovně zadané v jazyce Java Native Interface (JNI). Xamarin.Android poskytuje nástroj, který tyto vazby generuje. Tento nástroj umožňuje vývojáři řídit způsob vytváření vazby pomocí metadat, což umožňuje postupy, jako jsou úpravy oborů názvů a přejmenování členů. Tento dokument popisuje, jak metadata fungují, shrnuje atributy, které metadata podporují, a vysvětluje, jak vyřešit problémy s vazbou úpravou těchto metadat.

Přehled

Knihovna vazeb Jazyka Java pro Xamarin.Android se snaží automatizovat většinu práce potřebné pro vytvoření vazby existující knihovny Androidu pomocí nástroje, který se někdy označuje jako Generátor vazeb. Při vytváření vazby knihovny Java Xamarin.Android zkontroluje třídy Java a vygeneruje seznam všech balíčků, typů a členů, které mají být vázány. Tento seznam rozhraní API je uložený v souboru XML, který najdete v umístění {project directory}\obj\Release\api.xml pro sestavení RELEASE a v umístění {project directory}\obj\Debug\api.xml pro sestavení DEBUG .

Location of the api.xml file in the obj/Debug folder

Generátor vazeb použije soubor api.xml jako vodítko pro generování nezbytných tříd obálky jazyka C#. Obsah tohoto souboru XML je variantou formátu Open Source Projectu google. Následující fragment kódu je příkladem obsahu 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>

V tomto příkladu api.xml deklaruje třídu v android balíčku s názvem Manifest , který rozšiřuje java.lang.Object.

V mnoha případech je potřeba, aby se rozhraní Java API cítilo více jako .NET nebo aby se opravily problémy, které brání kompilaci sestavení vazby. Může být například nutné změnit názvy balíčků Java na obory názvů .NET, přejmenovat třídu nebo změnit návratový typ metody.

Tyto změny nejsou dosaženy úpravou api.xml přímo. Místo toho se změny zaznamenávají ve speciálních souborech XML, které poskytuje šablona knihovny vazeb Java. Při kompilaci sestavení vazby Xamarin.Android bude generátor vazeb ovlivněn těmito mapovacími soubory při vytváření sestavení vazby.

Tyto soubory mapování XML mohou být nalezeny ve složce Transforms projektu:

  • MetaData.xml – Umožňuje provádět změny v konečném rozhraní API, například změnit obor názvů vygenerované vazby.

  • EnumFields.xml – obsahuje mapování mezi konstantami Java int a jazykem C# enums .

  • EnumMethods.xml – Umožňuje měnit parametry metody a vracet typy z konstant Javy int do jazyka C# enums .

Soubor MetaData.xml je nejvíce importovaných těchto souborů, protože umožňuje změny vazby pro obecné účely, například:

  • Přejmenování oborů názvů, tříd, metod nebo polí tak, aby dodržovaly konvence .NET

  • Odebrání oborů názvů, tříd, metod nebo polí, která nejsou potřeba.

  • Přesouvání tříd do různých oborů názvů

  • Přidání dalších tříd podpory pro vytvoření návrhu vazby se řídí vzory rozhraní .NET Framework.

Pojďme se podrobněji věnovat Metadata.xml .

Transformační soubor Metadata.xml

Jak jsme se už naučili, soubor Metadata.xml používá Generátor vazeb k ovlivnění vytvoření sestavení vazby. Formát metadat používá syntaxi XPath a je téměř stejný jako metadata GAPI popsaná v průvodci metadaty GAPI. Tato implementace je téměř kompletní implementace XPath 1.0, a proto podporuje položky ve standardu 1.0. Tento soubor je výkonný mechanismus založený na XPathu pro změnu, přidání, skrytí nebo přesunutí libovolného prvku nebo atributu v souboru rozhraní API. Všechny prvky pravidla ve specifikaci metadat obsahují atribut cesty k identifikaci uzlu, na který se má pravidlo použít. Pravidla se použijí v následujícím pořadí:

  • add-node – připojí podřízený uzel k uzlu určenému atributem cesty.
  • attr – Nastaví hodnotu atributu prvku určeného atributem path.
  • remove-node – Odebere uzly odpovídající zadané cestě XPath.

Následuje příklad souboru 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>

Následující seznam uvádí některé běžně používané elementy XPath pro rozhraní JAVA API:

  • interface – Slouží k vyhledání rozhraní Java. Například /interface[@name='AuthListener'].

  • class – Slouží k vyhledání třídy . Například /class[@name='MapView'].

  • method – Slouží k vyhledání metody ve třídě nebo rozhraní Java. Například /class[@name='MapView']/method[@name='setTitleSource'].

  • parameter – Identifikujte parametr pro metodu. Například /parameter[@name='p0']

Přidání typů

Element add-node sdělí projektu vazby Xamarin.Android, aby do api.xml přidal novou třídu obálky. Následující fragment kódu například nasměruje Generátor vazeb na vytvoření třídy s konstruktorem a jedním polem:

<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>

Odebírání typů

Generátor vazeb Xamarin.Android je možné instruovat, aby ignoroval typ Javy a neváže ho. To se provádí přidáním elementu remove-node XML do souboru metadata.xml :

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

Přejmenování členů

Přejmenování členů nelze provést přímo úpravou souboru api.xml , protože Xamarin.Android vyžaduje původní názvy JNI (Java Native Interface). //class/@name Proto atribut nelze změnit. Pokud ano, vazba nebude fungovat.

Vezměte v úvahu případ, kdy chceme přejmenovat typ, android.Manifest. Abychom toho dosáhli, můžeme se pokusit přímo upravit api.xml a přejmenovat třídu takto:

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

Výsledkem bude generátor vazeb, který vytvoří následující kód jazyka C# pro třídu obálky:

[Register ("android/NewName")]
public class NewName : Java.Lang.Object { ... }

Všimněte si, že třída obálky byla přejmenována na NewName, zatímco původní typ Java je stále Manifest. Třída vazby Xamarin.Android již není možná pro přístup k žádným metodám android.Manifest. Třída obálky je vázána na neexistující typ Java.

Pokud chcete správně změnit spravovaný název zabaleného typu (nebo metody), je nutné nastavit managedName atribut, jak je znázorněno v tomto příkladu:

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

Přejmenování EventArg tříd obálky

Když generátor vazeb Xamarin.Android identifikuje metodu onXXX setter pro typ naslouchacího procesu, vygeneruje se událost jazyka C# a EventArgs podtřída pro podporu rozhraní API s příchutí .NET pro vzor naslouchacího procesu založeného na Javě. Jako příklad zvažte následující třídu a metodu Java:

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

Xamarin.Android přeloží předponu on z metody setter a místo toho se použije 2DSignNextManuever jako základ pro název EventArgs podtřídy. Podtřída bude mít název něco podobného:

NavigationManager.2DSignNextManueverEventArgs

Nejedná se o právní název třídy jazyka C#. Chcete-li tento problém vyřešit, musí autor vazby argsType použít atribut a zadat platný název jazyka C#pro podtřídu EventArgs :

<attr path="/api/package[@name='com.someapp.android.mpa.guidance']/
    interface[@name='NavigationManager.Listener']/
    method[@name='on2DSignNextManeuver']" 
    name="argsType">NavigationManager.TwoDSignNextManueverEventArgs</attr>

Podporované atributy

Následující části popisují některé atributy transformace rozhraní Java API.

argsType

Tento atribut se umístí do metod setter pro pojmenování EventArg podtřídy, která se vygeneruje pro podporu naslouchacích procesů Java. Toto je podrobněji popsáno níže v části Přejmenování tříd obálky EventArg dále v tomto průvodci.

eventName

Určuje název události. Pokud je prázdná, inhibuje generování událostí. Toto je podrobněji popsáno v části Přejmenování tříd obálky EventArg.

managedName

Slouží ke změně názvu balíčku, třídy, metody nebo parametru. Chcete-li například změnit název třídy MyClass Java na NewClassName:

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

Následující příklad znázorňuje výraz XPath pro přejmenování metody java.lang.object.toString na Java.Lang.Object.NewManagedName:

<attr path="/api/package[@name='java.lang']/class[@name='Object']/method[@name='toString']" 
    name="managedName">NewMethodName</attr>

managedType

managedType slouží ke změně návratového typu metody. V některých situacích generátor vazeb nesprávně odvodí návratový typ metody Java, což způsobí chybu v době kompilace. Jedním z možných řešení v této situaci je změnit návratový typ metody.

Například Bindings Generator věří, že metoda de.neom.neoreadersdk.resolution.compareTo() Java by měla vrátit int a vzít Object jako parametry, což má za následek chybovou zprávu Chyba CS0535: 'DE. Neom.Neoreadersdk.Resolution neimplementuje člena rozhraní Java.Lang.IComparable.CompareTo(Java.Lang.Object). Následující fragment kódu ukazuje, jak změnit typ prvního parametru vygenerované metody jazyka C# z metody na DE.Neom.Neoreadersdk.ResolutionJava.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

Změní návratový typ metody. Tím se nezmění návratový atribut (protože změny návratových atributů můžou vést k nekompatibilním změnám podpisu JNI). V následujícím příkladu se návratový append typ metody změní na SpannableStringBuilderIAppendable (vzpomeňte si, že jazyk C# nepodporuje kovariantní návratové typy):

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

Zakódovány

Nástroje, které obfuscate knihovny Java mohou kolidovat s generátorem vazeb Xamarin.Android a jeho schopnost generovat třídy obálky jazyka C#. Charakteristiky obfuskovaných tříd zahrnují:

  • Název třídy obsahuje , $tj. a$.class.
  • Název třídy je zcela ohrožen malá písmena, tj. a.class.

Tento fragment kódu je příkladem toho, jak vygenerovat typ C# bez obfuskování:

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

Propertyname

Tento atribut lze použít ke změně názvu spravované vlastnosti.

Specializovaný případ použití propertyName zahrnuje situaci, kdy třída Java má pouze metodu getter pro pole. V takovém případě by generátor vazeb chtěl vytvořit vlastnost jen pro zápis, což je něco, co se nedoporučuje v .NET. Následující fragment kódu ukazuje, jak "odebrat" vlastnosti .NET nastavením propertyName na prázdný řetězec:

<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>

Všimněte si, že metody setter a getter budou stále vytvořeny pomocí Bindings Generator.

Odesílatele

Určuje, který parametr metody má být sender parametrem při mapování metody na událost. Hodnota může být true nebo false. Příklad:

<attr path="/api/package[@name='android.app']/
    interface[@name='TimePickerDialog.OnTimeSetListener']/
    method[@name='onTimeSet']/
    parameter[@name='view']" 
    name="sender">true</ attr>

viditelnost

Tento atribut se používá ke změně viditelnosti třídy, metody nebo vlastnosti. Například může být nutné zvýšit úroveň protected metody Java tak, aby odpovídající obálka jazyka C# byla 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 a EnumMethods.xml

Existují případy, kdy knihovny Androidu používají celočíselné konstanty k reprezentaci stavů, které se předávají vlastnostem nebo metodám knihoven. V mnoha případech je užitečné svázat tyto celočíselné konstanty k výčtům v jazyce C#. K usnadnění tohoto mapování použijte soubory EnumFields.xml a EnumMethods.xml v projektu vazby.

Definování výčtu pomocí EnumFields.xml

Soubor EnumFields.xml obsahuje mapování mezi konstantami Java int a jazykem C# enums. Podívejme se na následující příklad výčtu jazyka C#, který se vytváří pro sadu int konstant:

<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>

Zde jsme vzali třídu SKRealReachSettings Java a definovali výčet jazyka C# volaný SKMeasurementUnit v oboru názvů Skobbler.Ngx.Map.RealReach. Položky field definují název konstanty Java (příklad UNIT_SECOND), název položky výčtu (příklad Second) a celočíselnou hodnotu reprezentovanou oběma entitami (příklad 0).

Definování metod Getter/Setter pomocí EnumMethods.xml

Soubor EnumMethods.xml umožňuje měnit parametry metody a vracet typy z konstant Javy int do jazyka C# enums. Jinými slovy, mapuje čtení a zápis výčtů jazyka C# (definovaných v souboru EnumFields.xml) na konstantu get a set metody Javaint.

Vzhledem k výčtu SKRealReachSettings definovanému výše by následující soubor EnumMethods.xml definoval getter/setter pro tento výčet:

<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>

První method řádek mapuje návratovou hodnotu metody Java getMeasurementUnit na SKMeasurementUnit výčet. Druhý method řádek mapuje první parametr stejného setMeasurementUnit výčtu.

Se všemi těmito změnami můžete v Xamarin.Androidu MeasurementUnitnastavit následující kód:

realReachSettings.MeasurementUnit = SKMeasurementUnit.Second;

Shrnutí

Tento článek popisuje, jak Xamarin.Android používá metadata k transformaci definice rozhraní API z formátu GoogleAOSP. Po pokrytí změn, které jsou možné pomocí Metadata.xml, prozkoumala omezení, ke kterým došlo při přejmenování členů, a zobrazil seznam podporovaných atributů XML popisující, kdy se má každý atribut použít.