Risoluzione dei problemi delle associazioni

Importante

Attualmente si sta esaminando l'utilizzo dell'associazione personalizzata nella piattaforma Xamarin. Si prega di prendere questo sondaggio per informare i futuri sforzi di sviluppo.

Questo articolo riepiloga gli errori comuni del server che possono verificarsi durante la generazione di associazioni, insieme a possibili cause e modi suggeriti per risolverli.

Panoramica

L'associazione di una libreria Android ( un file con estensione aar o un .jar) è raramente un problema semplice. In genere richiede ulteriore sforzo per attenuare i problemi risultanti dalle differenze tra Java e .NET. Questi problemi impediranno a Xamarin.Android di eseguire l'associazione della libreria Android e di presentarsi come messaggi di errore nel log di compilazione. Questa guida fornirà alcuni suggerimenti per la risoluzione dei problemi, elencare alcuni dei problemi/scenari più comuni e fornire possibili soluzioni per l'associazione corretta della libreria Android.

Quando si associa una libreria Android esistente, è necessario tenere presenti i punti seguenti:

  • Dipendenze esterne per la libreria : tutte le dipendenze Java richieste dalla libreria Android devono essere incluse nel progetto Xamarin.Android come ReferenceJar o come EmbeddedReferenceJar.

  • Livello api Android di destinazione della libreria Android: non è possibile effettuare il "downgrade" del livello api Android. Assicurarsi che il progetto di associazione Xamarin.Android sia destinato allo stesso livello API (o superiore) della libreria Android.

  • La versione di Android JDK usata per creare un pacchetto della libreria Android: gli errori di binding possono verificarsi se la libreria Android è stata compilata con una versione diversa di JDK rispetto a quella usata da Xamarin.Android. Se possibile, ricompilare la libreria Android usando la stessa versione di JDK usata dall'installazione di Xamarin.Android.

Il primo passaggio per la risoluzione dei problemi relativi all'associazione di una libreria Xamarin.Android consiste nell'abilitare l'output di MSBuild di diagnostica. Dopo aver abilitato l'output di diagnostica, ricompilare il progetto di associazione Xamarin.Android ed esaminare il log di compilazione per individuare indizi sulla causa del problema.

Può anche risultare utile decompilare la libreria Android ed esaminare i tipi e i metodi che Xamarin.Android sta tentando di associare. Questo argomento è illustrato in modo più dettagliato più avanti in questa guida.

Decompilazione di una libreria Android

Esaminare le classi e i metodi delle classi Java può fornire informazioni utili per l'associazione di una libreria. JD-GUI è un'utilità grafica in grado di visualizzare il codice sorgente Java dai file CLASS contenuti in un file JAR. Può essere eseguita come applicazione autonoma o come plug-in per IntelliJ o Eclipse.

Per decompilare una libreria Android, aprire . File JAR con il decompiler Java. Se la libreria è un oggetto . File AAR , è necessario estrarre il file classes.jar dal file di archivio. Di seguito è riportato uno screenshot di esempio dell'uso di JD-GUI per analizzare il file JAR di Picasso :

Using the Java Decompiler to analyze picasso-2.5.2.jar

Dopo aver decompilato la libreria Android, esaminare il codice sorgente. In generale, cercare :

  • Le classi con caratteristiche di offuscamento : 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
  • importistruzioni per le librerie senza riferimenti: identificare la libreria senza riferimenti e aggiungere tali dipendenze al progetto di associazione Xamarin.Android con un'azione di compilazione ReferenceJar o EmbedddedReferenceJar.

Nota

La decompilazione di una libreria Java può essere vietata o soggetta a restrizioni legali in base alle leggi locali o alla licenza con cui è stata pubblicata la libreria Java. Se necessario, integrare i servizi di un professionista legale prima di tentare di decompilare una libreria Java ed esaminare il codice sorgente.

Esaminare API.XML

Nell'ambito della compilazione di un progetto di associazione, Xamarin.Android genererà un nome di file XML obj/Debug/api.xml:

Generated api.xml under obj/Debug

Questo file fornisce un elenco di tutte le API Java che Xamarin.Android sta provando a eseguire l'associazione. Il contenuto di questo file consente di identificare i tipi o i metodi mancanti, l'associazione duplicata. Sebbene l'ispezione di questo file sia noiosa e dispendiosa in termini di tempo, può fornire indicazioni su ciò che potrebbe causare eventuali problemi di associazione. Ad esempio, api.xml potrebbe rivelare che una proprietà restituisce un tipo inappropriato o che esistono due tipi che condividono lo stesso nome gestito.

Problemi noti

Questa sezione elenca alcuni dei comuni messaggi di errore o sintomi che si verificano durante il tentativo di associare una libreria Android.

Problema: Mancata corrispondenza della versione Java

A volte i tipi non verranno generati o si verificheranno arresti anomali imprevisti perché si usa una versione più recente o precedente di Java rispetto a quella con cui è stata compilata la libreria. Ricompilare la libreria Android con la stessa versione di JDK usata dal progetto Xamarin.Android.

Problema: è necessaria almeno una libreria Java

Viene visualizzato l'errore "è necessaria almeno una libreria Java", anche se . È stato aggiunto il file JAR.

Possibile cause:

Assicurarsi che l'azione di compilazione sia impostata su EmbeddedJar. Poiché sono presenti più azioni di compilazione per . I file JAR (ad esempio InputJar, ReferenceJarEmbeddedJare EmbeddedReferenceJar), il generatore di associazioni non può indovinare automaticamente quale usare per impostazione predefinita. Per altre informazioni sulle azioni di compilazione, vedere Azioni di compilazione.

Problema: gli strumenti di associazione non possono caricare . Libreria JAR

Il generatore della libreria di binding non riesce a caricare . Libreria JAR.

Possibili cause

Alcuni. Le librerie JAR che usano l'offuscamento del codice (tramite strumenti come Proguard) non possono essere caricate dagli strumenti Java. Poiché lo strumento usa la reflection Java e la libreria di progettazione del codice di byte ASM, questi strumenti dipendenti possono rifiutare le librerie offuscate mentre gli strumenti di runtime Android possono passare. La soluzione alternativa consiste nell'associare a mano queste librerie anziché usare il generatore di associazioni.

Problema: tipi C# mancanti nell'output generato.

L'associazione .dll compilazioni ma mancano alcuni tipi Java o l'origine C# generata non viene compilata a causa di un errore che indica che sono presenti tipi mancanti.

Possibile cause:

Questo errore può verificarsi a causa di diversi motivi, come indicato di seguito:

  • La libreria associata può fare riferimento a una seconda libreria Java. Se l'API pubblica per la libreria associata usa tipi della seconda libreria, è necessario fare riferimento anche a un'associazione gestita per la seconda libreria.

  • È possibile che una libreria sia stata inserita a causa della reflection Java, simile al motivo dell'errore di caricamento della libreria precedente, causando il caricamento imprevisto dei metadati. Gli strumenti di Xamarin.Android non possono attualmente risolvere questa situazione. In tal caso, la libreria deve essere associata manualmente.

  • Si è verificato un bug nel runtime di .NET 4.0 che non è riuscito a caricare gli assembly quando dovrebbe essere presente. Questo problema è stato risolto nel runtime di .NET 4.5.

  • Java consente la derivazione di una classe pubblica da una classe non pubblica, ma questa classe non è supportata in .NET. Poiché il generatore di associazioni non genera associazioni per classi non pubbliche, le classi derivate come queste non possono essere generate correttamente. Per risolvere questo problema, rimuovere la voce di metadati per le classi derivate usando il nodo remove in Metadata.xml oppure correggere i metadati che rendono pubblica la classe non pubblica. Anche se quest'ultima soluzione creerà l'associazione in modo che l'origine C# venga compilata, la classe non pubblica non deve essere usata.

    Ad esempio:

    <attr path="/api/package[@name='com.some.package']/class[@name='SomeClass']"
        name="visibility">public</attr>
    
  • Gli strumenti che offuscano le librerie Java possono interferire con il generatore di binding Xamarin.Android e la sua capacità di generare classi wrapper C#. Il frammento di codice seguente illustra come aggiornare Metadata.xml per eliminare un nome di classe:

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

Problema: l'origine C# generata non viene compilata a causa della mancata corrispondenza del tipo di parametro

L'origine C# generata non viene compilata. I tipi di parametro del metodo sottoposto a override non corrispondono.

Possibile cause:

Xamarin.Android include un'ampia gamma di campi Java mappati alle enumerazioni nelle associazioni C#. Questi possono causare incompatibilità dei tipi nelle associazioni generate. Per risolvere questo problema, è necessario modificare le firme del metodo create dal generatore di associazioni per usare le enumerazioni. Per altre informazioni, vedere Correzione delle enumerazioni.

Problema: NoClassDefFoundError nella creazione del pacchetto

java.lang.NoClassDefFoundError viene generata nel passaggio di creazione del pacchetto.

Possibile cause:

Il motivo più probabile per questo errore è che è necessario aggiungere una libreria Java obbligatoria al progetto dell'applicazione (con estensione csproj). . I file JAR non vengono risolti automaticamente. Un'associazione di libreria Java non viene sempre generata su un assembly utente che non esiste nel dispositivo o nell'emulatore di destinazione ( ad esempio Google Mappe maps.jar). Questo non è il caso per il supporto del progetto libreria Android, come la libreria . JAR è incorporato nella dll della libreria.

Problema: Tipi EventArgs personalizzati duplicati

La compilazione non riesce a causa di tipi EventArgs personalizzati duplicati. Si verifica un errore simile al seguente:

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

Possibile cause:

Ciò è dovuto al fatto che si verifica un conflitto tra i tipi di evento provenienti da più tipi di interfaccia "listener" che condividono metodi con nomi identici. Ad esempio, se sono presenti due interfacce Java, come illustrato nell'esempio seguente, il generatore crea DismissScreenEventArgs per e MediationInterstitialListenerMediationBannerListener , generando l'errore .

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

Si tratta di una struttura che consente di evitare nomi lunghi sui tipi di argomenti dell'evento. Per evitare questi conflitti, è necessaria una trasformazione dei metadati. Modificare Transforms\Metadata.xml e aggiungere un argsType attributo in una delle interfacce (o nel metodo di interfaccia):

<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: la classe non implementa il metodo di interfaccia

Viene generato un messaggio di errore che indica che una classe generata non implementa un metodo necessario per un'interfaccia implementata dalla classe generata. Tuttavia, esaminando il codice generato, è possibile vedere che il metodo è implementato.

Di seguito è riportato un esempio dell'errore:

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'

Possibile cause:

Si tratta di un problema che si verifica con l'associazione di metodi Java con tipi restituiti covarianti. In questo esempio, il metodo Oauth.Signpost.Http.IHttpRequest.UnWrap() deve restituire Java.Lang.Object. Tuttavia, il metodo Oauth.Signpost.Basic.HttpURLConnectionRequestAdapter.UnWrap() ha un tipo restituito di HttpURLConnection. Esistono due modi per risolvere questo problema:

  • Aggiungere una dichiarazione di classe parziale per HttpURLConnectionRequestAdapter e implementare in modo esplicito IHttpRequest.Unwrap():

    namespace Oauth.Signpost.Basic {
        partial class HttpURLConnectionRequestAdapter {
            Java.Lang.Object OauthSignpost.Http.IHttpRequest.Unwrap() {
                return Unwrap();
            }
        }
    }
    
  • Rimuovere la covarianza dal codice C# generato. Ciò comporta l'aggiunta della trasformazione seguente a Transforms\Metadata.xml che causerà che il codice C# generato avrà un tipo restituito di Java.Lang.Object:

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

Problema: Assegnare un nome alle collisioni nelle classi/proprietà interne

Visibilità in conflitto sugli oggetti ereditati.

In Java non è necessario che una classe derivata abbia la stessa visibilità dell'elemento padre. Java risolverà automaticamente questo problema. In C# deve essere esplicito, quindi è necessario assicurarsi che tutte le classi nella gerarchia abbiano la visibilità appropriata. L'esempio seguente illustra come modificare il nome di un pacchetto Java da com.evernote.android.job a 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: una libreria con estensione so richiesta dall'associazione non viene caricata

Alcuni progetti di associazione possono dipendere anche dalla funzionalità in una libreria .so . È possibile che Xamarin.Android non caricherà automaticamente la libreria .so . Quando viene eseguito il codice Java sottoposto a wrapping, Xamarin.Android non riuscirà a eseguire la chiamata JNI e il messaggio di errore java.lang.UnsatisfiedLinkError: native method not found: verrà visualizzato nel logcat out per l'applicazione.

La correzione per questa operazione consiste nel caricare manualmente la libreria .so con una chiamata a Java.Lang.JavaSystem.LoadLibrary. Si supponga, ad esempio, che un progetto Xamarin.Android abbia una libreria condivisa libpocketsphinx_jni.so inclusa nel progetto di associazione con un'azione di compilazione EmbeddedNativeLibrary, il frammento di codice seguente (eseguito prima di usare la libreria condivisa) caricherà la libreria .so:

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

Riepilogo

In questo articolo sono stati elencati i problemi comuni di risoluzione dei problemi associati alle associazioni Java e sono stati illustrati come risolverli.