Risoluzione dei problemi relativi alle associazioni
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 .NET per 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 .NET per Android tramite un pacchetto NuGet o come AndroidLibrary.
Livello api Android di destinazione della libreria Android: non è possibile effettuare il "downgrade" del livello api Android. Assicurarsi che il progetto di associazione .NET per Android sia destinato allo stesso livello API (o superiore) della libreria Android.
Suggerimento
Il wiki del repository GitHub degli strumenti di associazione è un'ottima risorsa e contiene informazioni aggiuntive sulla risoluzione dei problemi che possono essere utili per casi specifici.
Il primo passaggio per risolvere i problemi relativi all'associazione di una libreria .NET per Android consiste nell'abilitare l'output di MSBuild di diagnostica. Dopo aver abilitato l'output di diagnostica, ricompilare il progetto di associazione .NET per Android ed esaminare il log di compilazione per individuare gli indizi sulla causa del problema.
Può anche risultare utile decompilare la libreria Android ed esaminare i tipi e i metodi che .NET per 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.
Per decompilare una libreria Android, aprire . File JAR con il decompiler Java. Se la libreria è un oggetto . File AAR , il codice sorgente Java si troverà nella voce classes.jar del file di archivio. Di seguito è riportato uno screenshot di esempio dell'uso di JD-GUI per analizzare il file JAR di Picasso :
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
import
istruzioni per librerie senza riferimenti: identificare la libreria senza riferimenti e aggiungere tali dipendenze al progetto di associazione .NET per Android con un'associazione appropriata da NuGet o con un'azione di compilazione di AndroidLibrary.
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, .NET per Android genererà un nome di file XML obj/Debug/api.xml:
Questo file fornisce un elenco di tutte le API Java che .NET per 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: 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.
Possibili 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.
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 .NET per 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.
Possibili cause:
.NET per 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 Creazione di enumerazioni.
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'
Possibili 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 MediationInterstitialListener
MediationBannerListener
, 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'
Possibili 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 esplicitoIHttpRequest.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 .so richiesta dall'associazione non viene caricata
Alcuni progetti di associazione possono dipendere anche dalla funzionalità in una libreria .so . È possibile che .NET per Android non caricherà automaticamente la libreria .so . Quando viene eseguito il codice Java sottoposto a wrapping, .NET per 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 .NET per Android abbia una libreria condivisa libpocketsphinx_jni.so inclusa nel progetto di associazione con un'azione di compilazione embeddedNativeLibrary, il frammento seguente (eseguito prima di usare la libreria condivisa) caricherà la libreria .so:
Java.Lang.JavaSystem.LoadLibrary("pocketsphinx_jni");