Metadati delle associazioni Java
Una libreria di binding .NET per Android Java tenta di automatizzare gran parte del lavoro necessario per associare una libreria Android esistente con l'aiuto di uno strumento talvolta noto come generatore di associazioni. Quando si associa una libreria Java, .NET per Android esamina le classi Java e genera un elenco di tutti i pacchetti, i tipi e i membri da associare. Questo elenco di API è archiviato in un file XML disponibile in {project directory}\obj{Configuration}\api.xml.
Il generatore di associazioni userà il file api.xml come linea guida per generare le classi wrapper C# necessarie. Il frammento di codice seguente è un esempio del contenuto di 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>
In questo esempio, api.xml dichiara una classe nel android
pacchetto denominato Manifest
che estende .java.lang.Object
In molti casi, l'assistenza umana è necessaria per rendere l'API Java più simile a ".NET" o per correggere i problemi che impediscono la compilazione dell'assembly di associazione. Ad esempio, potrebbe essere necessario modificare i nomi dei pacchetti Java in spazi dei nomi .NET, rinominare una classe o modificare il tipo restituito di un metodo.
Queste modifiche non devono essere apportate modificando direttamente api.xml . Le modifiche vengono invece registrate in file XML speciali forniti dal modello Libreria di binding Java. Quando si compila l'assembly di associazione .NET per Android, il generatore di associazioni verrà influenzato da questi file di mapping durante la creazione dell'assembly di associazione
Il file Metadata.xml è il più importante di questi file perché consente modifiche per utilizzo generico all'associazione, ad esempio:
Ridenominazione di spazi dei nomi, classi, metodi o campi in modo che seguano le convenzioni .NET.
Rimozione di spazi dei nomi, classi, metodi o campi non necessari.
Spostamento di classi in spazi dei nomi diversi.
L'aggiunta di classi di supporto aggiuntive per rendere la progettazione dell'associazione seguire i modelli .NET Framework.
Metadata.xml file di trasformazione
Come si è già appreso, il file Metadata.xml viene usato dal generatore di associazioni per influenzare la creazione dell'assembly di associazione. Il formato dei metadati usa la sintassi XPath .
Questa implementazione è quasi un'implementazione completa di XPath 1.0 e supporta quindi gli elementi nello standard 1.0. Questo file è un potente meccanismo basato su XPath per modificare, aggiungere, nascondere o spostare qualsiasi elemento o attributo nel file API. Tutti gli elementi della regola nella specifica di metadati includono un path
attributo per identificare i nodi a cui applicare la regola. Di seguito sono riportati i tipi di elemento disponibili:
- add-node : aggiunge un nodo figlio al nodo specificato dall'attributo path.
- attr : imposta il valore di un attributo dell'elemento specificato dall'attributo path.
- remove-node : rimuove i nodi corrispondenti a un XPath specificato.
Di seguito è riportato un esempio di file 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>
Di seguito sono elencati alcuni degli elementi XPath più comunemente usati per l'API Java:
interface
: usato per individuare un'interfaccia Java. ad esempio/interface[@name='AuthListener']
.class
: usato per individuare una classe . ad esempio/class[@name='MapView']
.method
: usato per individuare un metodo in una classe o un'interfaccia Java. ad esempio/class[@name='MapView']/method[@name='setTitleSource']
.parameter
: identificare un parametro per un metodo. Ad esempio:/parameter[@name='p0']
Aggiunta di tipi
L'elemento add-node
indicherà al progetto di associazione .NET per Android di aggiungere una nuova classe a api.xml. Ad esempio, il frammento di codice seguente indirizza il generatore di binding a creare una classe con un costruttore e un singolo campo:
<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>
Rimozione dei tipi
È possibile indicare a .NET for Android Bindings Generator di ignorare un tipo Java e non associarlo. A tale scopo, aggiungere un remove-node
elemento XML al file Metadata.xml :
<remove-node path="/api/package[@name='{package_name}']/class[@name='{name}']" />
Ridenominazione dei membri
Non è possibile rinominare i membri modificando direttamente il file api.xml perché .NET per Android richiede i nomi JNI (Java Native Interface) originali per comunicare con Java. Pertanto, l'attributo //class/@name
non può essere modificato. In caso affermativo, l'associazione non funzionerà.
Si consideri il caso in cui si vuole rinominare un tipo, android.Manifest
.
A tale scopo, è possibile provare a modificare direttamente api.xml e rinominare la classe in questo modo:
<attr path="/api/package[@name='android']/class[@name='Manifest']"
name="name">NewName</attr>
In questo modo, il generatore binding crea il codice C# seguente per la classe wrapper:
[Register ("android/NewName")]
public class NewName : Java.Lang.Object { ... }
Si noti che la classe wrapper è stata rinominata in NewName
, mentre il tipo Java originale è ancora Manifest
. Non è più possibile che la classe di associazione .NET per Android accesa a metodi in android.Manifest
. La classe wrapper è associata a un tipo Java inesistente.
Per modificare correttamente il nome "gestito" di un tipo di cui è stato eseguito il wrapping (o il metodo), è necessario impostare l'attributo managedName
come illustrato in questo esempio:
<attr path="/api/package[@name='android']/class[@name='Manifest']"
name="managedName">NewName</attr>
L'uso managedName
di è necessario quando si tenta di rinominare qualsiasi membro, ad esempio classi, interfacce, metodi e parametri.
Ridenominazione delle EventArg
classi wrapper
Quando il generatore di binding .NET per Android identifica un onXXX
metodo setter per un tipo di listener, verrà generato un evento E# e EventArgs
una sottoclasse C# per supportare un'API con gusto .NET per il modello di listener basato su Java. Si consideri ad esempio la classe e il metodo Java seguenti:
com.someapp.android.mpa.guidance.NavigationManager.on2DSignNextManuever(NextManueverListener listener);
.NET per Android rilascia il prefisso on
dal metodo setter e usa 2DSignNextManuever
invece come base per il nome della EventArgs
sottoclasse. La sottoclasse verrà denominata in modo simile a:
NavigationManager.2DSignNextManueverEventArgs
Non si tratta di un nome di classe C# legale. Per risolvere questo problema, l'autore dell'associazione deve usare l'attributo argsType
e specificare un nome C# valido per la EventArgs
sottoclasse:
<attr path="/api/package[@name='com.someapp.android.mpa.guidance']/
interface[@name='NavigationManager.Listener']/
method[@name='on2DSignNextManeuver']"
name="argsType">NavigationManager.TwoDSignNextManueverEventArgs</attr>
Attributi supportati
Le sezioni seguenti descrivono alcuni degli attributi per la trasformazione delle API Java.
argsType
Questo attributo viene inserito nei metodi setter per denominare la EventArg
sottoclasse che verrà generata per supportare i listener Java. Questo articolo è descritto in modo più dettagliato nella sezione Ridenominazione delle classi wrapper eventArg in questa guida.
eventName
Specifica un nome per un evento. Se il nome è vuoto, impedisce la generazione di eventi. Questo argomento è descritto in modo più dettagliato nella sezione Ridenominazione delle classi wrapper eventArg.
managedName
Viene usato per modificare il nome di un pacchetto, una classe, un metodo o un parametro. Ad esempio, per modificare il nome della classe MyClass
Java in NewClassName
:
<attr path="/api/package[@name='com.my.application']/class[@name='MyClass']"
name="managedName">NewClassName</attr>
Nell'esempio seguente viene illustrata un'espressione XPath per rinominare il metodo java.lang.object.toString
in Java.Lang.Object.NewManagedName
:
<attr path="/api/package[@name='java.lang']/class[@name='Object']/method[@name='toString']"
name="managedName">NewMethodName</attr>
managedType
managedType
viene utilizzato per modificare il tipo restituito di un metodo. In alcune situazioni il generatore di associazioni dedurrà erroneamente il tipo restituito di un metodo Java, che genererà un errore in fase di compilazione. Una possibile soluzione in questa situazione consiste nel modificare il tipo restituito del metodo.
Ad esempio, il generatore binding ritiene che il metodo de.neom.neoreadersdk.resolution.compareTo()
Java debba restituire e int
accettare Object
come parametri, che genera il messaggio di errore Error CS0535: 'DE. Neom.Neoreadersdk.Resolution' non implementa il membro dell'interfaccia 'Java.Lang.IComparable.CompareTo(Java.Lang.Object)'.
Il frammento di codice seguente illustra come modificare il tipo del primo parametro del metodo C# generato da a DE.Neom.Neoreadersdk.Resolution
un oggetto 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
Modifica il tipo restituito di un metodo. Ciò non modifica l'attributo restituito( poiché le modifiche apportate agli attributi restituiti possono comportare modifiche incompatibili alla firma JNI). Nell'esempio seguente il tipo restituito del append
metodo viene modificato da SpannableStringBuilder
a IAppendable
:
<attr path="/api/package[@name='android.text']/
class[@name='SpannableStringBuilder']/
method[@name='append']"
name="managedReturn">Java.Lang.IAppendable</attr>
Offuscato
Gli strumenti che offuscano le librerie Java possono interferire con il generatore di binding .NET per Android e la sua capacità di generare classi wrapper C#. Le caratteristiche delle classi offuscate includono:
- Il nome della classe include , $ad esempio a$.class
- Il nome della classe è completamente compromesso dai caratteri minuscoli, ad esempio a.class
Questo frammento di codice è un esempio di come generare un tipo C# non offuscato:
<attr path="/api/package[@name='{package_name}']/class[@name='{name}']"
name="obfuscated">false</attr>
propertyName
Questo attributo può essere usato per modificare il nome di una proprietà gestita.
Un caso specializzato di utilizzo propertyName
implica la situazione in cui una classe Java ha solo un metodo setter per un campo. In questo caso il generatore di binding vuole creare una proprietà di sola scrittura, un elemento sconsigliato in .NET. Il frammento di codice seguente illustra come "rimuovere" le proprietà .NET impostando su propertyName
una stringa vuota:
<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>
Si noti che i metodi setter e getter verranno comunque creati dal generatore di associazioni, ma non verranno convertiti in una proprietà .NET.
mittente
Specifica quale parametro di un metodo deve essere il sender
parametro quando il metodo viene mappato a un evento. Il valore può essere true
o false
. Ad esempio:
<attr path="/api/package[@name='android.app']/
interface[@name='TimePickerDialog.OnTimeSetListener']/
method[@name='onTimeSet']/
parameter[@name='view']"
name="sender">true</ attr>
visibility
Questo attributo viene usato per modificare la visibilità di una classe, di un metodo o di una proprietà. Ad esempio, potrebbe essere necessario alzare di livello un protected
metodo Java in modo che il wrapper C# corrispondente sia 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>