Compartilhar via


Solução de problemas de associação

Importante

No momento, estamos investigando o uso de vinculação personalizada na plataforma Xamarin. Por favor, faça esta pesquisa para informar os futuros esforços de desenvolvimento.

Este artigo resume os erros comuns do servidor que podem ocorrer ao gerar associações, juntamente com possíveis causas e maneiras sugeridas de resolvê-los.

Visão geral

Vincular um arquivo de biblioteca Android (um .aar ou um arquivo .jar) raramente é algo simples, geralmente requer um esforço adicional para mitigar problemas resultantes das diferenças entre Java e .NET. Esses problemas impedirão que o Xamarin.Android vincule a biblioteca do Android e se apresente como mensagens de erro no log de compilação. Este guia fornecerá algumas dicas para solucionar os problemas, listar alguns dos problemas/cenários mais comuns e fornecer possíveis soluções para vincular com êxito a biblioteca do Android.

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

  • As dependências externas para a biblioteca – Todas as dependências Java exigidas pela biblioteca Android devem ser incluídas no projeto Xamarin.Android como um ReferenceJar ou como um EmbeddedReferenceJar.

  • O nível de API do Android que a biblioteca do Android está atrasando – Não é possível "downgrade" do nível da API do Android, certifique-se de que o projeto de vinculação do Xamarin.Android esteja visando o mesmo nível de API (ou superior) que a biblioteca do Android.

  • A versão do JDK do Android que foi usada para empacotar a biblioteca do Android – Erros de vinculação podem ocorrer se a biblioteca do Android foi construída com uma versão diferente do JDK que a usada pelo Xamarin.Android. Se possível, recompile a biblioteca do Android usando a mesma versão do JDK usada pela instalação do Xamarin.Android.

A primeira etapa para solucionar problemas com a vinculação de uma biblioteca Xamarin.Android é habilitar a saída de diagnóstico do MSBuild. Depois de habilitar a saída de diagnóstico, recrie o projeto de vinculação Xamarin.Android e examine o log de compilação para localizar pistas sobre qual é a causa do problema.

Também pode ser útil descompilar a biblioteca do Android e examinar os tipos e métodos que o Xamarin.Android está tentando vincular. Isso será abordado com mais detalhes mais adiante neste guia.

Descompilando uma biblioteca Android

Inspecionar as classes e métodos das classes Java pode fornecer informações valiosas que ajudarão na vinculação de uma biblioteca. JD-GUI é um utilitário gráfico que pode exibir o código-fonte Java dos arquivos CLASS contidos em um JAR. Ele pode ser executado como um aplicativo autônomo ou como um plug-in para IntelliJ ou Eclipse.

Para descompilar uma biblioteca Android, abra o . Arquivo JAR com o descompilador Java. Se a biblioteca for um arquivo . AAR , é necessário extrair o arquivo classes.jar do arquivo morto. A seguir está uma captura de tela de exemplo do uso do JD-GUI para analisar o Picasso JAR:

Using the Java Decompiler to analyze picasso-2.5.2.jar

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

  • Classes que têm características de ofuscação – As características das classes ofuscadas incluem:

    • O nome da classe inclui um $, ou seja, a$.class
    • O nome da classe é totalmente comprometido de 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 vinculação Xamarin.Android com uma Ação de compilação de ReferenceJar ou EmbedddedReferenceJar.

Observação

A descompilação de uma biblioteca Java pode ser proibida ou sujeita a restrições legais com base nas leis locais ou na licença sob a qual a biblioteca Java foi publicada. Se necessário, conte com os 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 criação de um projeto de vinculação, o Xamarin.Android gerará um nome de arquivo XML obj/Debug/api.xml:

Generated api.xml under obj/Debug

Esse arquivo fornece uma lista de todas as APIs Java que o Xamarin.Android está tentando vincular. O conteúdo desse arquivo pode ajudar a identificar quaisquer tipos ou métodos ausentes, associação duplicada. Embora a inspeção desse arquivo seja tediosa e demorada, ela pode fornecer pistas sobre o que pode estar causando problemas de vinculação. Por exemplo, api.xml pode revelar que uma propriedade está retornando um tipo inadequado ou que há dois tipos que compartilham o mesmo nome gerenciado.

Problemas conhecidos

Esta seção listará algumas das mensagens de erro comuns ou sintomas que ocorrem ao tentar vincular uma biblioteca do Android.

Problema: Incompatibilidade de versão Java

Às vezes, os tipos não serão gerados ou falhas inesperadas podem ocorrer porque você está usando uma versão mais recente ou mais antiga do Java em comparação com o que a biblioteca foi compilada. Recompile a biblioteca do Android com a mesma versão do JDK que seu projeto Xamarin.Android está usando.

Problema: Pelo menos uma biblioteca Java é necessária

Você recebe o erro "pelo menos uma biblioteca Java é necessária", mesmo que um arquivo . JAR foi adicionado.

Causas possíveis:

Verifique se a ação de compilação está definida como EmbeddedJar. Uma vez que há várias ações de compilação para o . Arquivos JAR (como InputJar, EmbeddedJarReferenceJar e EmbeddedReferenceJar), o gerador de vinculação não pode adivinhar automaticamente qual deles usar por padrão. Para obter mais informações sobre ações de compilação, consulte Ações de compilação.

Problema: as ferramentas de vinculação não podem carregar o arquivo . Biblioteca JAR

O gerador de biblioteca de vinculação falha ao carregar o arquivo . Biblioteca JAR.

Causas possíveis

Alguns. As bibliotecas JAR que usam ofuscação de código (por meio de ferramentas como o Proguard) não podem ser carregadas pelas ferramentas Java. Como nossa ferramenta faz uso da reflexão Java e da biblioteca de engenharia de código de bytes ASM, essas ferramentas dependentes podem rejeitar as bibliotecas ofuscadas enquanto as ferramentas de tempo de execução do Android podem passar. A solução alternativa para isso é vincular manualmente essas bibliotecas em vez de usar o gerador de vinculação.

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

A ligação .dll compila, mas perde alguns tipos Java, ou o código-fonte C# gerado não é compilado devido a um erro informando que há tipos ausentes.

Causas possíveis:

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

  • A biblioteca que está sendo vinculada pode fazer referência a uma segunda biblioteca Java. Se a API pública para a biblioteca vinculada usar tipos da segunda biblioteca, você também deverá fazer referência a uma associação gerenciada para a segunda biblioteca.

  • É possível que uma biblioteca tenha sido injetada devido à reflexão Java, semelhante ao motivo do erro de carregamento da biblioteca acima, causando o carregamento inesperado de metadados. As ferramentas do Xamarin.Android não podem resolver essa situação no momento. Nesse caso, a biblioteca deve ser vinculada manualmente.

  • Houve um bug no tempo de execução do .NET 4.0 que falhou ao carregar assemblies quando deveria. Esse problema foi corrigido no tempo de execução do .NET 4.5.

  • Java permite derivar uma classe pública de classe não pública, mas isso não é suportado no .NET. Como o gerador de vinculação não gera associações para classes não públicas, classes derivadas como essas não podem ser geradas corretamente. Para corrigir isso, remova a entrada de metadados dessas classes derivadas usando o nó remover no Metadata.xml ou corrija os metadados que estão tornando a classe não pública pública. Embora a última solução crie a associação para que o código-fonte C# seja compilado, a classe não pública não deve ser usada.

    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 Xamarin.Android Binding Generator e sua capacidade de gerar classes wrapper C#. O trecho a seguir mostra como atualizar Metadata.xml para desocultar um nome de classe:

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

Problema: O código-fonte C# gerado não é compilado devido à incompatibilidade de tipo de parâmetro

A fonte C# gerada não é compilada. Os tipos de parâmetros do método substituído não coincidem.

Causas possíveis:

O Xamarin.Android inclui uma variedade de campos Java que são mapeados para enums nas associações C#. Isso pode causar incompatibilidades de tipo nas ligações geradas. Para resolver isso, as assinaturas de método criadas a partir do gerador de vinculação precisam ser modificadas para usar os enums. Para obter mais informações, consulte Corrigindo Enums.

Problema: NoClassDefFoundError no empacotamento

java.lang.NoClassDefFoundError é lançado na etapa de empacotamento.

Causas possíveis:

A razão mais provável para esse erro é que uma biblioteca Java obrigatória precisa ser adicionada ao projeto do aplicativo (.csproj). . Os arquivos JAR não são resolvidos automaticamente. Uma associação de biblioteca Java nem sempre é gerada em um assembly de usuário que não existe no dispositivo ou emulador de destino (como o Google Maps maps.jar). Este não é o caso para o suporte ao projeto Biblioteca Android, como a biblioteca . JAR é incorporado na biblioteca dll.

Problema: Duplicar tipos de EventArgs personalizados

A compilação falha devido a tipos EventArgs personalizados duplicados. Ocorre um erro como este:

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

Causas possíveis:

Isso ocorre porque há algum conflito entre tipos de evento que vêm de mais de um tipo de "ouvinte" de interface que compartilha métodos com nomes idênticos. Por exemplo, se houver duas interfaces Java, como visto no exemplo abaixo, o gerador criará DismissScreenEventArgs para ambas MediationBannerListener e MediationInterstitialListener, resultando no erro.

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

Isso ocorre por design para que nomes longos em tipos de argumento de evento sejam evitados. Para evitar esses conflitos, é necessária alguma transformação de metadados. Edite Transformações\Metadata.xml e adicione um argsType atributo em 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

Uma mensagem de erro é produzida indicando que uma classe gerada não implementa um método que é necessário para uma interface que a classe gerada implementa. No entanto, olhando para o código gerado, você 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:

Esse é um problema que ocorre com a vinculação de métodos Java com tipos de retorno covariante. Neste exemplo, o método Oauth.Signpost.Http.IHttpRequest.UnWrap() precisa retornar Java.Lang.Object. No entanto, o método Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.UnWrap() tem um tipo de retorno de HttpURLConnection. Há duas maneiras de corrigir esse 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. Isso envolve adicionar a seguinte transformação a Transforms\Metadata.xml o 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 nome em classes internas / propriedades

Visibilidade conflitante em objetos herdados.

Em Java, não é necessário que uma classe derivada tenha a mesma visibilidade que seu pai. Java irá apenas corrigir isso para você. Em C#, isso precisa ser explícito, então você precisa garantir que todas as classes na hierarquia tenham a visibilidade apropriada. O exemplo a seguir mostra como alterar um nome de 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 vinculação não está carregando

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

A correção para isso é carregar manualmente a biblioteca .so com uma chamada para Java.Lang.JavaSystem.LoadLibrary. Por exemplo, supondo que um projeto Xamarin.Android tenha biblioteca compartilhada libpocketsphinx_jni.so incluída no projeto de vinculação com uma ação de compilação de EmbeddedNativeLibrary, o seguinte trecho (executado antes de usar a biblioteca compartilhada) carregará a biblioteca .so:

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

Resumo

Neste artigo, listamos problemas comuns de solução de problemas associados a Java Bindings e explicamos como resolvê-los.