Partilhar via


Resolução de problemas de associações

Vincular um ficheiro de biblioteca Android ( .aar ou .jar) raramente é uma tarefa simples; normalmente requer esforço adicional para mitigar problemas resultantes das diferenças entre Java e .NET. Estes problemas vão impedir que o .NET para Android vincule a biblioteca Android e se apresentem como mensagens de erro no registo de compilação. Este guia fornecerá algumas dicas para resolver problemas, listará alguns dos cenários mais comuns e fornecerá possíveis soluções para associar com sucesso a biblioteca Android.

Ao anexar uma biblioteca Android existente, é necessário ter em conta os seguintes pontos:

  • As dependências externas da biblioteca – Quaisquer dependências de Java exigidas pela biblioteca Android devem ser incluídas no projeto .NET para Android através de um pacote NuGet ou como uma AndroidLibrary.

  • O nível da API Android que a biblioteca Android pretende atingir – Não é possível "downgrade" do nível da API Android; garantir que o projeto de binding .NET for Android tem como alvo o mesmo nível de API (ou superior) da biblioteca Android.

Sugestão

A wiki do repositório GitHub do Binding Tooling é um excelente recurso e contém informações adicionais de resolução de problemas que podem ajudar em casos específicos.

O primeiro passo para resolver problemas ao ligar uma biblioteca .NET para Android é ativar a saída de diagnóstico do MSBuild. Depois de ativar a saída de diagnóstico, reconstrua o projeto de ligação .NET para Android e examine o registo de compilação para encontrar pistas sobre a causa do problema.

Também pode ser útil descompilar a biblioteca Android e examinar os tipos e métodos que o .NET para Android tenta vincular. Isto é abordado com mais detalhe mais adiante neste guia.

Descompilar uma biblioteca Android

Inspecionar as classes e métodos das classes Java pode fornecer informações valiosas que ajudam a vincular uma biblioteca. O JD-GUI é uma utilidade gráfica que pode mostrar código-fonte Java a partir dos ficheiros CLASS contidos num JAR.

Para descompilar uma biblioteca Android, abra o arquivo . JAR com o descompilador Java. Se a biblioteca for um .AAR, o código-fonte Java estará na entrada classes.jar do ficheiro. Segue-se uma captura de ecrã de exemplo de utilização de JD-GUI para analisar o JAR de Picasso :

Usar o descompilador Java para analisar picasso-2.5.2.jar

Depois de descompilar a biblioteca Android, examine o código-fonte. De um modo geral, procure por:

  • Classes que apresentam características de ofuscação – 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
  • import instruções para bibliotecas não referenciadas – Identifique a biblioteca não referenciada e adicione essas dependências ao projeto de binding .NET para Android com uma binding apropriada do NuGet ou com uma Ação de Construção do AndroidLibrary.

Observação

Descompilar uma biblioteca Java pode ser proibido ou sujeito a restrições legais com base nas leis locais ou na licença sob a qual a biblioteca Java foi publicada. Se necessário, recorra aos serviços de um profissional jurídico antes de tentar descompilar uma biblioteca Java e inspecionar o código-fonte.

Inspecionar api.xml

Como parte da construção de um projeto de binding, o .NET para Android gera um nome de ficheiro XML obj/Debug/api.xml:

Gerado api.xml em obj/Debug

Este ficheiro fornece uma lista de todas as APIs Java que o .NET para Android está a tentar vincular. O conteúdo deste ficheiro pode ajudar a identificar tipos ou métodos em falta, ou ligação duplicada. Embora a inspeção deste ficheiro seja tediosa e demorada, pode fornecer pistas sobre o que poderá estar a causar problemas de encunção. Por exemplo, api.xml pode revelar que uma propriedade está a devolver um tipo inadequado, ou que existem dois tipos que partilham o mesmo nome gerido.

Problemas conhecidos

Esta secção irá listar algumas das mensagens de erro ou sintomas mais comuns que podem ocorrer ao tentar associar uma biblioteca Android.

Problema: Falta de tipos C# na saída gerada.

O ficheiro .dll é compilado, mas falta alguns tipos de Java, ou o código-fonte C# gerado não é compilado devido a um erro que indica que há tipos em falta.

Causas possíveis:

Este erro pode ocorrer por várias razões, conforme listado abaixo:

  • A biblioteca a ser encadernada pode referenciar uma segunda biblioteca Java. Se a API pública da biblioteca encadernada usar tipos da segunda biblioteca, deve também consultar uma ligação gerida para a segunda biblioteca.

  • Java permite derivar uma classe pública a partir de uma classe não pública, mas isso não é suportado em .NET. Como o gerador de ligações não gera ligações para classes não públicas, classes derivadas como estas não podem ser geradas corretamente. Para corrigir isto, ou remove a entrada de metadados para essas classes derivadas usando o remove-node em Metadata.xml, ou corrige os metadados que estão a tornar pública a classe não pública. Embora esta última solução crie a vinculação para que o código-fonte C# possa ser compilado, a classe não pública não deve ser utilizada.

    Por exemplo:

    <attr path="/api/package[@name='com.some.package']/class[@name='SomeClass']"
        name="visibility">public</attr>
    
  • Ferramentas que ofuscam bibliotecas Java podem interferir com o .NET for Android Binding Generator e a sua capacidade de gerar classes wrapper em C#. O seguinte excerto mostra como atualizar Metadata.xml para desofuscar o nome de uma classe.

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

Problema: A fonte gerada em C# não se compila devido a uma incompatibilidade no tipo de parâmetro

A fonte gerada em C# não compila. Os tipos de parâmetros do método sobrescrito não coincidem.

Causas possíveis:

O .NET para Android inclui uma variedade de campos Java que são mapeados para enums nas ligações C#. Estas podem causar incompatibilidades de tipo nas ligações geradas. Para resolver isto, as assinaturas de método criadas pelo gerador de ligação precisam de ser modificadas para usar os enums. Para mais informações, consulte Criação de enumerações.

Problema: Duplicar tipos personalizados de EventArgs

A compilação falha devido a tipos duplicados de EventArgs personalizados. Ocorre um erro deste tipo:

error CS0102: The type `Com.Google.Ads.Mediation.DismissScreenEventArgs' already contains a definition for `p0'

Causas possíveis:

Isto deve-se ao facto de haver algum conflito entre tipos de eventos que resultam de mais do que um tipo de interface "ouvinte" que partilha métodos com nomes idênticos. Por exemplo, se existirem duas interfaces Java como visto no exemplo abaixo, o gerador cria DismissScreenEventArgs para ambos MediationBannerListener e MediationInterstitialListener, resultando no erro.

// Java:
public interface MediationBannerListener {
    void onDismissScreen(MediationBannerAdapter p0);
}
public interface MediationInterstitialListener {
    void onDismissScreen(MediationInterstitialAdapter p0);
}

Isto é intencional para evitar nomes longos nos tipos de argumentos do evento. Para evitar estes conflitos, é necessária alguma transformação dos metadados. Editar Transforma\Metadata.xml e adiciona um argsType atributo em qualquer uma das interfaces (ou no método de interface):

<attr path="/api/package[@name='com.google.ads.mediation']/
        interface[@name='MediationBannerListener']/method[@name='onDismissScreen']"
        name="argsType">BannerDismissScreenEventArgs</attr>

<attr path="/api/package[@name='com.google.ads.mediation']/
        interface[@name='MediationInterstitialListener']/method[@name='onDismissScreen']"
        name="argsType">IntersitionalDismissScreenEventArgs</attr>

<attr path="/api/package[@name='android.content']/
        interface[@name='DialogInterface.OnClickListener']"
        name="argsType">DialogClickEventArgs</attr>

Problema: A classe não implementa o método de interface

É produzida uma mensagem de erro indicando que uma classe gerada não implementa o método necessário para uma interface que a classe gerada implementa. No entanto, ao olhar para o código gerado, pode ver que o método está implementado.

Aqui está um exemplo do erro:

obj\Debug\generated\src\Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.cs(8,23):
error CS0738: 'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter' does not
implement interface member 'Oauth.Signpost.Http.IHttpRequest.Unwrap()'.
'Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.Unwrap()' cannot implement
'Oauth.Signpost.Http.IHttpRequest.Unwrap()' because it does not have the matching
return type of 'Java.Lang.Object'

Causas possíveis:

Este é um problema que ocorre ao ligar métodos Java com tipos de retorno covariantes. Neste exemplo, o método Oauth.Signpost.Http.IHttpRequest.UnWrap() precisa de devolver Java.Lang.Object. No entanto, o método Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.UnWrap() tem um tipo de retorno de HttpURLConnection. Existem duas formas de resolver este problema:

  • Adicione uma declaração de classe parcial para HttpURLConnectionRequestAdapter e implemente IHttpRequest.Unwrap()explicitamente :

    namespace Oauth.Signpost.Basic {
        partial class HttpURLConnectionRequestAdapter {
            Java.Lang.Object OauthSignpost.Http.IHttpRequest.Unwrap() {
                return Unwrap();
            }
        }
    }
    
  • Remova a covariância do código C# gerado. Isto envolve adicionar a seguinte transformação às Transforms\Metadata.xml que fará com que o código C# gerado tenha um tipo de retorno de Java.Lang.Object:

    <attr
        path="/api/package[@name='oauth.signpost.basic']/class[@name='HttpURLConnectionRequestAdapter']/method[@name='unwrap']"
        name="managedReturn">Java.Lang.Object
    </attr>
    

Problema: Colisões de nomes nas classes internas / propriedades

Visibilidade conflitante em objetos herdados.

Em Java, não é obrigatório que uma classe derivada tenha a mesma visibilidade que a sua mãe. O Java resolve isso por ti. Em C#, isso tem de ser explícito, por isso tens de garantir que todas as classes na hierarquia têm a visibilidade adequada. O exemplo seguinte mostra como alterar o nome de um pacote Java de com.evernote.android.job para Evernote.AndroidJob:

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

Problema: Uma biblioteca .so exigida pela ligação não está a carregar

Alguns projetos de binding podem também depender da funcionalidade numa biblioteca .so . É possível que o .NET para Android não carregue automaticamente a biblioteca .so . Quando o código Java encapsulado for executado, o .NET para Android falhará em fazer a chamada JNI e a mensagem de erro java.lang.UnsatisfiedLinkError: Método nativo não encontrado: aparecerá no logcat out da aplicação.

A solução para isto é carregar manualmente a biblioteca .so com uma chamada para Java.Lang.JavaSystem.LoadLibrary. Por exemplo, assumindo que um projeto .NET para Android tem a biblioteca partilhada libpocketsphinx_jni.so incluída no projeto binding com uma ação de compilação do EmbeddedNativeLibrary, o seguinte snippet (executado antes de usar a biblioteca partilhada) carregará a biblioteca .so :

Java.Lang.JavaSystem.LoadLibrary("pocketsphinx_jni");