Compartilhar via


Metadados de ligações Java

Uma biblioteca de associação Java do .NET para Android tenta automatizar grande parte do trabalho necessário para associar uma biblioteca Android existente com a ajuda de uma ferramenta às vezes conhecida como Gerador de Associações. Ao associar uma biblioteca Java, o .NET para Android inspecionará as classes Java e gerará uma lista de todos os pacotes, tipos e membros a serem associados. Essa lista de APIs é armazenada em um arquivo XML que pode ser encontrado em {diretório do projeto}\obj{Configuration}\api.xml.

Localização do arquivo api.xml na pasta obj/Debug

O Gerador de Associações usará o arquivo api.xml como uma diretriz para gerar as classes de wrapper C# necessárias. O snippet a seguir é um exemplo do conteúdo de 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>

Neste exemplo, api.xml declara uma classe no pacote android, nomeada Manifest, que estende java.lang.Object.

Em muitos casos, a assistência humana é necessária para fazer com que a API Java pareça mais "semelhante ao .NET" ou para corrigir problemas que impedem a compilação do assembly de associação. Por exemplo, pode ser necessário alterar nomes de pacotes Java para namespaces .NET, renomear uma classe ou alterar o tipo de retorno de um método.

Essas mudanças não devem ser alcançadas modificando api.xml diretamente. Em vez disso, as alterações são registradas em arquivos XML especiais fornecidos pelo modelo Java Binding Library. Ao compilar o assembly de associação do .NET para Android, o Gerador de Associações será influenciado por esses arquivos de mapeamento ao criar o assembly de associação

O arquivo Metadata.xml é o mais importante desses arquivos, pois permite alterações de uso geral na associação, como:

  • Renomear namespaces, classes, métodos ou campos para que eles sigam as convenções do .NET.

  • Remover namespaces, classes, métodos ou campos que não são necessários.

  • Movendo classes para namespaces diferentes.

  • Adicionar classes de suporte adicionais para fazer com que o design da associação siga os padrões do .NET Framework.

Arquivo de transformação Metadata.xml

Como já aprendemos, o arquivo Metadata.xml é usado pelo Gerador de Associações para influenciar a criação do assembly de associação. O formato de metadados usa a sintaxe XPath .

Essa implementação é quase uma implementação completa do XPath 1.0 e, portanto, suporta itens no padrão 1.0. Esse arquivo é um poderoso mecanismo baseado em XPath para alterar, adicionar, ocultar ou mover qualquer elemento ou atributo no arquivo de API. Todos os elementos de regra na especificação de metadados incluem um path atributo para identificar os nós aos quais a regra deve ser aplicada. A seguir estão os tipos de elementos disponíveis:

  • add-node – Anexa um nó filho ao nó especificado pelo atributo path.
  • attr – Define o valor de um atributo do elemento especificado pelo atributo path.
  • remove-node – Remove os nós que correspondem a um XPath especificado.

Veja a seguir um exemplo de um arquivo 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>

A seguir, listamos alguns dos elementos XPath mais comumente usados para as APIs Java:

  • interface – Usado para localizar uma interface Java. Por exemplo: /interface[@name='AuthListener'].

  • class – Usado para localizar uma classe . Por exemplo: /class[@name='MapView'].

  • method – Usado para localizar um método em uma classe ou interface Java. Por exemplo: /class[@name='MapView']/method[@name='setTitleSource'].

  • parameter – Identifique um parâmetro para um método. Por exemplo: /parameter[@name='p0']

Adicionando tipos

O add-node elemento informará ao projeto de associação .NET para Android para adicionar uma nova classe a api.xml. Por exemplo, o snippet a seguir direcionará o Gerador de Associação para criar uma classe com um construtor e um único 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>

Removendo tipos

É possível instruir o .NET for Android Bindings Generator a ignorar um tipo Java e não associá-lo. Isso é feito adicionando um remove-node elemento XML ao arquivo Metadata.xml :

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

Renomeando membros

A renomeação de membros não pode ser feita editando diretamente o arquivo api.xml porque o .NET para Android requer os nomes originais da JNI (Interface Nativa Java) para se comunicar com o Java. Portanto, o //class/@name atributo não pode ser alterado; se for, a associação não funcionará.

Considere o caso em que queremos renomear um tipo, android.Manifest. Para fazer isso, podemos tentar editar diretamente api.xml e renomear a classe assim:

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

Isso resultará na criação do Gerador de Associações do seguinte código C# para a classe wrapper:

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

Observe que a classe wrapper foi renomeada para NewName, enquanto o tipo Java original ainda é Manifest. Não é mais possível que a classe de associação .NET para Android acesse nenhum método em android.Manifest; a classe wrapper está associada a um tipo Java inexistente.

Para alterar corretamente o nome "gerenciado" de um tipo encapsulado (ou método), é necessário definir o managedName atributo conforme mostrado neste exemplo:

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

O uso managedName é necessário ao tentar renomear qualquer membro, como classes, interfaces, métodos e parâmetros.

Renomeando EventArg classes de wrapper

Quando o gerador de associações do .NET para Android identifica um método setter para um tipo de ouvinte, um evento e uma subclasse C# serão gerados para dar suporte a uma API no estilo do .NET para o padrão de ouvinte baseado em Java. Como exemplo, considere a seguinte classe e método Java:

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

O .NET para Android removerá o prefixo on do método setter e, em vez disso, usará 2DSignNextManuever como base para o EventArgs nome da subclasse. A subclasse terá um nome semelhante a:

NavigationManager.2DSignNextManueverEventArgs

Esse não é um nome de classe C# legal. Para corrigir esse problema, o autor da associação deve usar o argsType atributo e fornecer um nome C# válido para a EventArgs subclasse:

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

Atributos com suporte

As seções a seguir descrevem alguns dos atributos para transformar APIs Java.

argsType

Esse atributo é colocado em métodos setter para nomear a EventArg subclasse que será gerada para suportar ouvintes Java. Isso é descrito com mais detalhes na seção Renomeando classes de wrapper EventArg neste guia.

eventName

Especifica um nome para um evento. Se o nome estiver vazio, ele impedirá a geração de eventos. Isso é descrito com mais detalhes na seção Renomeando classes de wrapper EventArg.

nome_gerenciado

Isso é usado para alterar o nome de um pacote, classe, método ou parâmetro. Por exemplo, para alterar o nome da classe MyClass Java para NewClassName:

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

O exemplo a seguir ilustra uma expressão XPath para renomear o método java.lang.object.toString para Java.Lang.Object.NewManagedName:

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

tipo gerenciado

managedType é usado para alterar o tipo de retorno de um método. Em algumas situações, o Gerador de Associações inferirá incorretamente o tipo de retorno de um método Java, o que resultará em um erro de tempo de compilação. Uma solução possível nessa situação é alterar o tipo de retorno do método.

Por exemplo, o Gerador de Ligações considera que o método de.neom.neoreadersdk.resolution.compareTo() Java deve retornar um int e aceitar Object como parâmetros, o que resulta na mensagem de erro CS0535: 'DE.Neom.Neoreadersdk.Resolution' não implementa o membro da interface 'Java.Lang.IComparable.CompareTo(Java.Lang.Object)'. O snippet a seguir demonstra como alterar o tipo do primeiro parâmetro do método C# gerado de a DE.Neom.Neoreadersdk.Resolution para a 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> 

retornoGerenciado

Altera o tipo de retorno de um método. Isso não altera o atributo de retorno (pois as alterações nos atributos de retorno podem resultar em alterações incompatíveis na assinatura JNI). No exemplo a seguir, o tipo de retorno do append método é alterado de SpannableStringBuilder para IAppendable:

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

Ofuscado

As ferramentas que ofuscam bibliotecas Java podem interferir no Gerador de Associação do .NET para Android e em sua capacidade de gerar classes wrapper C#. As características das classes ofuscadas incluem:

  • O nome da classe inclui um $, ou seja , a$.class
  • O nome da classe é totalmente composto por caracteres minúsculos, ou seja, a.class

Este snippet é um exemplo de como gerar um tipo C# "não ofuscado":

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

nomeDaPropriedade

Esse atributo pode ser usado para alterar o nome de uma propriedade gerenciada.

Um caso especializado de uso propertyName envolve a situação em que uma classe Java tem apenas um método setter para um campo. Nessa situação, o Binding Generator pretende criar uma propriedade somente gravação, algo que é desencorajado no .NET. O snippet a seguir mostra como "remover" as propriedades do .NET definindo o propertyName como uma cadeia de caracteres vazia:

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

Observe que os métodos setter e getter ainda serão criados pelo Gerador de Associações, eles simplesmente não serão convertidos em uma propriedade .NET.

remetente

Especifica qual parâmetro de um método deve ser o sender parâmetro quando o método é mapeado para um evento. O valor pode ser true ou false. Por exemplo:

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

visibilidade

Esse atributo é usado para alterar a visibilidade de uma classe, método ou propriedade. Por exemplo, pode ser necessário promover um protected método Java para que seu wrapper C# correspondente seja 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>