Udostępnij za pośrednictwem


Metadane powiązań języka Java

Ważne

Obecnie badamy użycie powiązań niestandardowych na platformie Xamarin. Weź udział w tej ankiecie , aby poinformować o przyszłych wysiłkach programistycznych.

Kod języka C# na platformie Xamarin.Android wywołuje biblioteki Java za pomocą powiązań, które są mechanizmem, który stanowi abstrakcję szczegółów niskiego poziomu określonych w interfejsie natywnym języka Java (JNI). Platforma Xamarin.Android udostępnia narzędzie, które generuje te powiązania. To narzędzie pozwala deweloperowi kontrolować sposób tworzenia powiązania przy użyciu metadanych, które umożliwiają procedury, takie jak modyfikowanie przestrzeni nazw i zmienianie nazw elementów członkowskich. W tym dokumencie omówiono sposób działania metadanych, podsumowują atrybuty obsługiwane przez metadane i wyjaśniono, jak rozwiązywać problemy z powiązaniem, modyfikując te metadane.

Omówienie

Biblioteka powiązań języka Java platformy Xamarin.Android próbuje zautomatyzować większość pracy niezbędnej do powiązania istniejącej biblioteki systemu Android za pomocą narzędzia nazywanego czasami generatorem powiązań. Po powiązaniu biblioteki Języka Java platforma Xamarin.Android sprawdzi klasy Języka Java i wygeneruje listę wszystkich pakietów, typów i elementów członkowskich, które mają być powiązane. Ta lista interfejsów API jest przechowywana w pliku XML, który można znaleźć w katalogu {project directory}\obj\Release\api.xml dla kompilacji RELEASE i w katalogu {project directory}\obj\Debug\api.xml dla kompilacji DEBUGowania .

Lokalizacja pliku api.xml w folderze obj/Debug

Generator powiązań użyje pliku api.xml jako wytycznych dotyczących generowania niezbędnych klas otoki języka C#. Zawartość tego pliku XML jest odmianą formatu projektu Open Source systemu Google. Poniższy fragment kodu jest przykładem zawartości 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>

W tym przykładzie api.xml deklaruje klasę w android pakiecie o nazwie Manifest , która rozszerza klasę java.lang.Object.

W wielu przypadkach pomoc człowieka jest wymagana, aby interfejs API Języka Java czuł się bardziej jak .NET" lub aby rozwiązać problemy, które uniemożliwiają kompilowanie zestawu powiązania. Na przykład może być konieczne zmianę nazw pakietów Java na przestrzenie nazw platformy .NET, zmianę nazwy klasy lub zmianę zwracanego typu metody.

Te zmiany nie są osiągane przez bezpośrednie modyfikowanie api.xml . Zamiast tego zmiany są rejestrowane w specjalnych plikach XML udostępnianych przez szablon biblioteki powiązań Języka Java. Podczas kompilowania zestawu powiązań Xamarin.Android generator powiązań będzie mieć wpływ na te pliki mapowania podczas tworzenia zestawu powiązania

Te pliki mapowania XML można znaleźć w folderze Przekształcenia projektu:

  • MetaData.xml — umożliwia wprowadzanie zmian w ostatnim interfejsie API, takich jak zmiana przestrzeni nazw wygenerowanego powiązania.

  • EnumFields.xml — zawiera mapowanie między stałymi języka Java int i C# enums .

  • EnumMethods.xml — umożliwia zmianę parametrów metody i zwracanych typów z stałych Języka Java int do języka C# enums .

Plik MetaData.xml jest najbardziej importem tych plików, ponieważ umożliwia zmianę ogólnego przeznaczenia w powiązaniu, takie jak:

  • Zmiana nazw przestrzeni nazw, klas, metod lub pól, tak aby były zgodne z konwencjami platformy .NET.

  • Usuwanie przestrzeni nazw, klas, metod lub pól, które nie są potrzebne.

  • Przenoszenie klas do różnych przestrzeni nazw.

  • Dodanie dodatkowych klas obsługi w celu wykonania projektu powiązania zgodnie z wzorcami platformy .NET Framework.

Przejdźmy do bardziej szczegółowego omówienia Metadata.xml .

plik przekształcenia Metadata.xml

Jak już dowiedzieliśmy się, plik Metadata.xml jest używany przez generator powiązań w celu wywierania wpływu na tworzenie zestawu powiązania. Format metadanych używa składni XPath i jest prawie identyczny z metadanymi GAPI opisanymi w przewodniku po metadanych GAPI. Ta implementacja jest prawie kompletną implementacją środowiska XPath 1.0 i dlatego obsługuje elementy w standardzie 1.0. Ten plik jest zaawansowanym mechanizmem opartym na programie XPath umożliwiającym zmianę, dodawanie, ukrywanie lub przenoszenie dowolnego elementu lub atrybutu w pliku interfejsu API. Wszystkie elementy reguły w specyfikacji metadanych zawierają atrybut ścieżki w celu zidentyfikowania węzła, do którego ma zostać zastosowana reguła. Reguły są stosowane w następującej kolejności:

  • add-node — dołącza węzeł podrzędny do węzła określonego przez atrybut path.
  • attr — ustawia wartość atrybutu elementu określonego przez atrybut path.
  • remove-node — usuwa węzły zgodne z określoną ścieżką XPath.

Poniżej przedstawiono przykład pliku 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>

Poniżej wymieniono niektóre z najczęściej używanych elementów XPath dla interfejsu API języka Java:

  • interface — służy do lokalizowania interfejsu Java. na przykład /interface[@name='AuthListener'].

  • class — służy do lokalizowania klasy . na przykład /class[@name='MapView'].

  • method — służy do lokalizowania metody w klasie lub interfejsie Języka Java. na przykład /class[@name='MapView']/method[@name='setTitleSource'].

  • parameter — Zidentyfikuj parametr dla metody. Na przykład: /parameter[@name='p0']

Dodawanie typów

Element add-node poinformuje projekt powiązania platformy Xamarin.Android, aby dodać nową klasę otoki do api.xml. Na przykład poniższy fragment kodu przekierowuje generator powiązań, aby utworzyć klasę z konstruktorem i pojedynczym 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>

Usuwanie typów

Istnieje możliwość poinstruowania generatora powiązań platformy Xamarin.Android, aby zignorował typ języka Java i nie powiązał go. W tym celu należy dodać remove-node element XML do pliku metadata.xml :

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

Zmienianie nazw członków

Zmiana nazw elementów członkowskich nie może być wykonywana bezpośrednio przez edytowanie pliku api.xml, ponieważ platforma Xamarin.Android wymaga oryginalnych nazw interfejsu natywnego Java (JNI). W związku z tym nie można zmienić atrybutu //class/@name ; jeśli tak jest, powiązanie nie będzie działać.

Rozważmy przypadek, w którym chcemy zmienić nazwę typu , android.Manifest. W tym celu możemy spróbować bezpośrednio edytować api.xml i zmienić nazwę klasy w następujący sposób:

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

Spowoduje to utworzenie przez generator powiązań następującego kodu języka C# dla klasy otoki:

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

Zwróć uwagę, że nazwa klasy otoki została zmieniona na NewName, a oryginalny typ języka Java to nadal Manifest. Nie jest już możliwe, aby klasa powiązania Xamarin.Android uzyskiwała dostęp do dowolnych metod w android.Manifestsystemie ; klasa otoki jest powiązana z nieistniejący typem języka Java.

Aby poprawnie zmienić nazwę zarządzaną typu opakowanego (lub metody), należy ustawić managedName atrybut, jak pokazano w tym przykładzie:

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

EventArg Zmienianie nazw klas otoki

Gdy generator powiązań platformy Xamarin.Android zidentyfikuje onXXX metodę ustawiającą dla typu odbiornika, zdarzenie języka C# i EventArgs podklasa zostaną wygenerowane w celu obsługi interfejsu API o smaku platformy .NET dla wzorca odbiornika opartego na języku Java. Rozważmy na przykład następującą klasę i metodę Języka Java:

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

Platforma Xamarin.Android usunie prefiks on z metody setter i zamiast tego użyje 2DSignNextManuever jako podstawy nazwy podklasy EventArgs . Podklasa będzie mieć nazwę podobną do następującej:

NavigationManager.2DSignNextManueverEventArgs

To nie jest legalna nazwa klasy języka C#. Aby rozwiązać ten problem, autor powiązania musi użyć atrybutu argsType i podać prawidłową nazwę języka C# dla podklasy EventArgs :

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

Obsługiwane atrybuty

W poniższych sekcjach opisano niektóre atrybuty służące do przekształcania interfejsów API języka Java.

argsType

Ten atrybut jest umieszczany w metodach ustawiających, aby nazwać podklasę EventArg , która zostanie wygenerowana w celu obsługi odbiorników Języka Java. Opisano to bardziej szczegółowo poniżej w sekcji Zmiana nazwy klas otoki EventArg w dalszej części tego przewodnika.

eventName

Określa nazwę zdarzenia. Jeśli jest pusty, hamuje generowanie zdarzeń. Opisano to bardziej szczegółowo w tytule sekcji Zmiana nazwy klas otoki EventArg.

managedName

Służy do zmiany nazwy pakietu, klasy, metody lub parametru. Aby na przykład zmienić nazwę klasy MyClass Java na NewClassName:

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

W następnym przykładzie pokazano wyrażenie XPath służące do zmiany nazwy 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 Służy do zmiany zwracanego typu metody. W niektórych sytuacjach generator powiązań niepoprawnie wywnioskuje zwracany typ metody Java, co spowoduje błąd czasu kompilacji. Jednym z możliwych rozwiązań w tej sytuacji jest zmiana typu zwracanego metody.

Na przykład generator powiązań uważa, że metoda de.neom.neoreadersdk.resolution.compareTo() Java powinna zwrócić int parametr i przyjąć Object jako parametry, co powoduje komunikat o błędzie Błąd CS0535: "DE. Neom.Neoreadersdk.Resolution" nie implementuje elementu członkowskiego interfejsu "Java.Lang.IComparable.CompareTo(Java.Lang.Object)". Poniższy fragment kodu pokazuje, jak zmienić typ pierwszego parametru wygenerowanej metody języka C# z klasy na 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

Zmienia zwracany typ metody. Nie powoduje to zmiany atrybutu zwracanego (ponieważ zmiany zwracanych atrybutów mogą spowodować niezgodne zmiany w podpisie JNI). W poniższym przykładzie zwracany typ append metody jest zmieniany z SpannableStringBuilder na IAppendable (przypomnij sobie, że język C# nie obsługuje kowariantnych typów zwracanych):

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

Ukrywane

Narzędzia, które zaciemniają biblioteki Języka Java, mogą zakłócać działanie generatora powiązań platformy Xamarin.Android i możliwość generowania klas otoki języka C#. Cechy zaciemnionych klas obejmują:

  • Nazwa klasy zawiera element $, tj. $.class
  • Nazwa klasy jest całkowicie naruszona z małymi literami, tj. klasa

Ten fragment kodu to przykład generowania typu C# "bez zaciemnionego":

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

propertyName

Ten atrybut może służyć do zmiany nazwy właściwości zarządzanej.

Wyspecjalizowany przypadek użycia propertyName obejmuje sytuację, w której klasa Java ma tylko metodę getter dla pola. W takiej sytuacji generator powiązań chciałby utworzyć właściwość tylko do zapisu, co jest odradzane na platformie .NET. Poniższy fragment kodu pokazuje, jak "usunąć" właściwości platformy .NET przez ustawienie wartości na propertyName pusty ciąg:

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

Należy pamiętać, że metody ustawiające i getter będą nadal tworzone przez generator powiązań.

nadawca

Określa, który parametr metody powinien być parametrem sender , gdy metoda jest mapowana na zdarzenie. Wartość może mieć wartość true lub false. Na przykład:

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

widoczność

Ten atrybut służy do zmiany widoczności klasy, metody lub właściwości. Na przykład może być konieczne podwyższenie poziomu protected metody Java, tak aby odpowiednia otoka języka C# to 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 i EnumMethods.xml

Istnieją przypadki, w których biblioteki systemu Android używają stałych całkowitych do reprezentowania stanów przekazywanych do właściwości lub metod bibliotek. W wielu przypadkach warto powiązać te stałe całkowite z wyliczeniami w języku C#. Aby ułatwić to mapowanie, użyj plików EnumFields.xml i EnumMethods.xml w projekcie powiązania.

Definiowanie wyliczenia przy użyciu EnumFields.xml

Plik EnumFields.xml zawiera mapowanie między stałymi języka Java int i C# enums. Przyjrzyjmy się następującemu przykładowi wyliczenia języka C# tworzonego dla zestawu int stałych:

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

W tym miejscu zajęliśmy klasę SKRealReachSettings Java i zdefiniowaliśmy wyliczenie języka C# o nazwie SKMeasurementUnit w przestrzeni nazw Skobbler.Ngx.Map.RealReach. Wpisy field definiują nazwę stałej Java (przykład UNIT_SECOND), nazwę wpisu wyliczenia (przykład Second) i wartość całkowitą reprezentowaną przez obie jednostki (przykład 0).

Definiowanie metod Getter/Setter przy użyciu EnumMethods.xml

Plik EnumMethods.xml umożliwia zmianę parametrów metody i zwracanych typów z stałych Java int na C# enums. Innymi słowy, mapuje odczyt i zapis wyliczenia języka C# (zdefiniowane w pliku EnumFields.xml) na stałe get i set metody języka Javaint.

Biorąc pod uwagę wyliczenie SKRealReachSettings zdefiniowane powyżej, następujący plik EnumMethods.xml zdefiniuje metodę getter/setter dla tego wyliczenia:

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

Pierwszy method wiersz mapuje wartość zwracaną metody Java getMeasurementUnit na wyliczenie SKMeasurementUnit . Drugi method wiersz mapuje pierwszy parametr obiektu setMeasurementUnit na ten sam wyliczenie.

Po wprowadzeniu wszystkich tych zmian można użyć następującego kodu na platformie Xamarin.Android, aby ustawić element MeasurementUnit:

realReachSettings.MeasurementUnit = SKMeasurementUnit.Second;

Podsumowanie

W tym artykule omówiono sposób, w jaki platforma Xamarin.Android używa metadanych do przekształcania definicji interfejsu API z formatu Google AOSP. Po pokryciu zmian, które są możliwe przy użyciu Metadata.xml, zbadał ograniczenia napotkane podczas zmiany nazwy elementów członkowskich i przedstawił listę obsługiwanych atrybutów XML, opisując, kiedy należy użyć każdego atrybutu.