Share via


Suggerimenti per la risoluzione dei problemi

Recupero di informazioni di diagnostica

Xamarin.Android offre alcune posizioni da esaminare durante il rilevamento di vari bug. tra cui:

  1. Output di MSBuild di diagnostica.
  2. Log di distribuzione dei dispositivi.
  3. Output del log di debug Android.

Output di MSBuild di diagnostica

Diagnostica MSBuild può contenere informazioni aggiuntive relative alla compilazione di pacchetti e possono contenere alcune informazioni sulla distribuzione dei pacchetti.

Per abilitare l'output di MSBuild di diagnostica in Visual Studio:

  1. Fare clic su Opzioni strumenti > ...
  2. Nella visualizzazione albero a sinistra selezionare Progetti ed esecuzione soluzioni >
  3. Nel pannello di destra impostare l'elenco a discesa Dettaglio output compilazione MSBuild su Diagnostica
  4. Fare clic su OK.
  5. Pulire e ricompilare il pacchetto.
  6. L'output di diagnostica è visibile all'interno del pannello Output.

Per abilitare l'output di MSBuild di diagnostica all'interno di Visual Studio per Mac/OS X:

  1. Fare clic su Preferenze Visual Studio per Mac>...
  2. Nella visualizzazione albero a sinistra selezionare Progetti > Compilazione
  3. Nel pannello di destra impostare l'elenco a discesa Dettagli log su Diagnostica
  4. Fare clic su OK.
  5. Riavviare Visual Studio per Mac
  6. Pulire e ricompilare il pacchetto.
  7. L'output di diagnostica è visibile all'interno del riquadro errori (visualizza > errori > ), facendo clic sul pulsante Compila output.

Log di distribuzione dei dispositivi

Per abilitare la registrazione della distribuzione dei dispositivi in Visual Studio:

  1. Opzioni strumenti > ...>
  2. Nella visualizzazione albero a sinistra selezionare Xamarin > Android Impostazioni
  3. Nel pannello di destra abilitare la casella di controllo [X] extension debug logging (scrive monodroid.log sul desktop).
  4. I messaggi di log vengono scritti nel file monodroid.log sul desktop.

Visual Studio per Mac scrive sempre i log di distribuzione dei dispositivi. FInding loro è leggermente più difficile; Viene creato un file di log AndroidUtils per ogni giorno + ora in cui si verifica una distribuzione, ad esempio: AndroidTools-2012-10-24_12-35-45.log.

  • In Windows i file di log vengono scritti in %LOCALAPPDATA%\XamarinStudio-{VERSION}\Logs.
  • In OS X i file di log vengono scritti in $HOME/Library/Logs/XamarinStudio-{VERSION}.

Android Debug Log Output

Android scriverà molti messaggi nel log di debug Android. Xamarin.Android usa le proprietà di sistema Android per controllare la generazione di messaggi aggiuntivi nel log di debug Android. Le proprietà del sistema Android possono essere impostate tramite il comando setprop all'interno di Android Debug Bridge (adb):Android system properties can be set through the setprop command within the Android Debug Bridge (adb):

adb shell setprop PROPERTY_NAME PROPERTY_VALUE

Le proprietà di sistema vengono lette durante l'avvio del processo e pertanto devono essere impostate prima dell'avvio dell'applicazione oppure l'applicazione deve essere riavviata dopo la modifica delle proprietà di sistema.

Proprietà di sistema di Xamarin.Android

Xamarin.Android supporta le proprietà di sistema seguenti:

  • debug.mono.debug: se una stringa non vuota, equivale a *mono-debug*.

  • debug.mono.env: elenco delimitato da pipe (''|) di variabili di ambiente da esportare durante l'avvio dell'applicazione, prima dell'inizializzazione di mono. Ciò consente di impostare le variabili di ambiente che controllano la registrazione mono.

    Nota

    Poiché il valore è "|separato", il valore deve avere un livello aggiuntivo di virgolette, perché il comando 'adb shell' rimuoverà un set di virgolette.

    Nota

    I valori delle proprietà di sistema Android non possono contenere più di 92 caratteri.

    Esempio:

    adb shell setprop debug.mono.env "'MONO_LOG_LEVEL=info|MONO_LOG_MASK=asm'"
    
  • debug.mono.log: elenco delimitato da virgole (',') di componenti che devono stampare messaggi aggiuntivi nel log di debug Android. Per impostazione predefinita, non viene impostato alcun valore. I componenti includono:

    • all: Stampa tutti i messaggi
    • gc: stampare messaggi correlati a GC.
    • gref: stampare (debole, globale) messaggi di allocazione e deallocazione dei riferimenti.
    • lref: consente di stampare messaggi di allocazione e deallocazione dei riferimenti locali.

    Nota

    Queste sono estremamente verbose. Non abilitare a meno che non sia effettivamente necessario.

  • debug.mono.trace: consente di impostare l'impostazione mono --trace=PROPERTY_VALUE .

Eliminazione bin e obj

Xamarin.Android ha subito in passato da una situazione come:

  • Si verifica un errore di compilazione o di runtime strano.
  • È Cleanpossibile Rebuildeliminare manualmente le bin directory e obj .
  • Il problema va via.

Abbiamo investito molto per risolvere problemi come questi a causa del loro impatto sulla produttività degli sviluppatori.

Se si verifica un problema come questo:

  1. Prendi nota mentale. Qual è stata l'ultima azione che ha portato il progetto in questo stato?
  2. Salvare il log di compilazione corrente. Riprovare a compilare e registrare un log di compilazione di diagnostica.
  3. Inviare un report di bug.

Prima di eliminare le bin directory e obj , comprimerle e salvarle per una diagnosi successiva, se necessario. È probabile che Clean il progetto di applicazione Xamarin.Android funzioni di nuovo.

Xamarin.Android non è in grado di risolvere System.ValueTuple

Questo errore si verifica a causa di un'incompatibilità con Visual Studio.

  • Visual Studio 2017 Update 1 (versione 15.1 o precedente) è compatibile solo con System.ValueTuple NuGet 4.3.0 (o versione precedente).

  • Visual Studio 2017 Update 2 (versione 15.2 o successiva) è compatibile solo con System.ValueTuple NuGet 4.3.1 (o versione successiva).

Scegliere il NuGet System.ValueTuple corretto corrispondente all'installazione di Visual Studio 2017.

Messaggi GC

I messaggi dei componenti GC possono essere visualizzati impostando la proprietà di sistema debug.mono.log su un valore che contiene gc.

I messaggi GC vengono generati ogni volta che il GC viene eseguito e fornisce informazioni sulla quantità di lavoro eseguita dal GC:

I/monodroid-gc(12331): GC cleanup summary: 81 objects tested - resurrecting 21.

È possibile generare informazioni aggiuntive sul GC, ad esempio informazioni sulla tempistica, impostando la MONO_LOG_LEVEL variabile di ambiente su debug:

adb shell setprop debug.mono.env MONO_LOG_LEVEL=debug

Ciò comporterà (molti) messaggi Mono aggiuntivi, inclusi questi tre di conseguenza:

D/Mono (15723): GC_BRIDGE num-objects 1 num_hash_entries 81226 sccs size 81223 init 0.00ms df1 285.36ms sort 38.56ms dfs2 50.04ms setup-cb 9.95ms free-data 106.54ms user-cb 20.12ms clenanup 0.05ms links 5523436/5523436/5523096/1 dfs passes 1104 6883/11046605
D/Mono (15723): GC_MINOR: (Nursery full) pause 2.01ms, total 287.45ms, bridge 225.60 promoted 0K major 325184K los 1816K
D/Mono ( 2073): GC_MAJOR: (user request) pause 2.17ms, total 2.47ms, bridge 28.77 major 576K/576K los 0K/16K

GC_BRIDGE Nel messaggio è num-objects il numero di oggetti bridge che questo passaggio sta considerando ed num_hash_entries è il numero di oggetti elaborati durante questa chiamata del codice bridge.

GC_MINOR Nei messaggi total e GC_MAJOR è la quantità di tempo durante la sospensione del mondo (nessun thread è in esecuzione), mentre bridge è la quantità di tempo impiegato nel codice di elaborazione bridge (che gestisce la macchina virtuale Java). Il mondo non viene sospeso durante l'elaborazione del bridge.

In generale, maggiore è il valore di num_hash_entries, maggiore sarà il tempo necessario per le bridge raccolte e maggiore sarà il total tempo impiegato per la raccolta.

Messaggi di riferimento globali

Per abilitare la registrazione global reference loggig (GREF), la proprietà di sistema debug.mono.log deve contenere gref, ad esempio:

adb shell setprop debug.mono.log gref

Xamarin.Android usa riferimenti globali Android per fornire mapping tra istanze Java e le istanze gestite associate, come quando si richiama un metodo Java è necessario fornire un'istanza Java a Java.

Sfortunatamente, gli emulatori Android consentono l'esistenza di 2000 riferimenti globali alla volta. L'hardware ha un limite molto superiore di 52000 riferimenti globali. Il limite inferiore può essere problematico quando si eseguono applicazioni nell'emulatore, quindi sapere da dove proviene l'istanza può essere molto utile.

Nota

Il conteggio dei riferimenti globali è interno a Xamarin.Android e non include (e non può) includere riferimenti globali acquisiti da altre librerie native caricate nel processo. Usare il conteggio dei riferimenti globali come stima.

I/monodroid-gref(12405): +g+ grefc 108 gwrefc 0 obj-handle 0x40517468/L -> new-handle 0x40517468/L from    at Java.Lang.Object.RegisterInstance(IJavaObject instance, IntPtr value, JniHandleOwnership transfer)
I/monodroid-gref(12405):    at Java.Lang.Object.SetHandle(IntPtr value, JniHandleOwnership transfer)
I/monodroid-gref(12405):    at Java.Lang.Object..ctor(IntPtr handle, JniHandleOwnership transfer)
I/monodroid-gref(12405):    at Java.Lang.Thread+RunnableImplementor..ctor(System.Action handler, Boolean removable)
I/monodroid-gref(12405):    at Java.Lang.Thread+RunnableImplementor..ctor(System.Action handler)
I/monodroid-gref(12405):    at Android.App.Activity.RunOnUiThread(System.Action action)
I/monodroid-gref(12405):    at Mono.Samples.Hello.HelloActivity.UseLotsOfMemory(Android.Widget.TextView textview)
I/monodroid-gref(12405):    at Mono.Samples.Hello.HelloActivity.<OnCreate>m__3(System.Object o)
I/monodroid-gref(12405): handle 0x40517468; key_handle 0x40517468: Java Type: `mono/java/lang/RunnableImplementor`; MCW type: `Java.Lang.Thread+RunnableImplementor`
I/monodroid-gref(12405): Disposing handle 0x40517468
I/monodroid-gref(12405): -g- grefc 107 gwrefc 0 handle 0x40517468/L from    at Java.Lang.Object.Dispose(System.Object instance, IntPtr handle, IntPtr key_handle, JObjectRefType handle_type)
I/monodroid-gref(12405):    at Java.Lang.Object.Dispose()
I/monodroid-gref(12405):    at Java.Lang.Thread+RunnableImplementor.Run()
I/monodroid-gref(12405):    at Java.Lang.IRunnableInvoker.n_Run(IntPtr jnienv, IntPtr native__this)
I/monodroid-gref(12405):    at System.Object.c200fe6f-ac33-441b-a3a0-47659e3f6750(IntPtr , IntPtr )
I/monodroid-gref(27679): +w+ grefc 1916 gwrefc 296 obj-handle 0x406b2b98/G -> new-handle 0xde68f4bf/W from take_weak_global_ref_jni
I/monodroid-gref(27679): -w- grefc 1915 gwrefc 294 handle 0xde691aaf/W from take_global_ref_jni

Ci sono quattro messaggi di conseguenza:

  • Creazione di riferimenti globali: si tratta delle righe che iniziano con +g+ e fornirà un'analisi dello stack per il percorso di creazione del codice.
  • Distruzione dei riferimenti globali: si tratta delle righe che iniziano con -g- e possono fornire un'analisi dello stack per il percorso del codice eliminando il riferimento globale. Se il GC sta eliminando il gref, non verrà fornita alcuna traccia dello stack.
  • Creazione di riferimenti globali deboli: si tratta delle righe che iniziano con +w+ .
  • Distruzione debole dei riferimenti globali: si tratta di righe che iniziano con -w- .

In tutti i messaggi il valore grefc è il numero di riferimenti globali creati da Xamarin.Android, mentre il valore grefwc è il conteggio dei riferimenti globali deboli creati da Xamarin.Android. Il valore handle o obj-handle è il valore dell'handle JNI e il carattere dopo '' /è il tipo di valore handle: /L per riferimento locale, /G per i riferimenti globali e /W per riferimenti globali deboli.

Come parte del processo GC, i riferimenti globali (+g+) vengono convertiti in riferimenti globali deboli (causando un +w+ e -g-), viene eseguito un GC sul lato Java e quindi viene controllato il riferimento globale debole per verificare se è stato raccolto. Se è ancora vivo, viene creato un nuovo gref intorno al ref debole (+g+, -w-), altrimenti il ref debole viene eliminato (-w).

L'istanza Java viene creata e sottoposta a wrapping da un MCW

I/monodroid-gref(27679): +g+ grefc 2211 gwrefc 0 obj-handle 0x4066df10/L -> new-handle 0x4066df10/L from ...
I/monodroid-gref(27679): handle 0x4066df10; key_handle 0x4066df10: Java Type: `android/graphics/drawable/TransitionDrawable`; MCW type: `Android.Graphics.Drawables.TransitionDrawable`

Viene eseguito un GC...

I/monodroid-gref(27679): +w+ grefc 1953 gwrefc 259 obj-handle 0x4066df10/G -> new-handle 0xde68f95f/W from take_weak_global_ref_jni
I/monodroid-gref(27679): -g- grefc 1952 gwrefc 259 handle 0x4066df10/G from take_weak_global_ref_jni

L'oggetto è ancora attivo, come handle != null

wref è tornato in un gref

I/monodroid-gref(27679): *try_take_global obj=0x4976f080 -> wref=0xde68f95f handle=0x4066df10
I/monodroid-gref(27679): +g+ grefc 1930 gwrefc 39 obj-handle 0xde68f95f/W -> new-handle 0x4066df10/G from take_global_ref_jni
I/monodroid-gref(27679): -w- grefc 1930 gwrefc 38 handle 0xde68f95f/W from take_global_ref_jni

L'oggetto è inattivo, come handle == null

wref viene liberato, non viene creato alcun nuovo gref

I/monodroid-gref(27679): *try_take_global obj=0x4976f080 -> wref=0xde68f95f handle=0x0
I/monodroid-gref(27679): -w- grefc 1914 gwrefc 296 handle 0xde68f95f/W from take_global_ref_jni

C'è una ruga "interessante" qui: sulle destinazioni che eseguono Android prima di 4.0, il valore gref è uguale all'indirizzo dell'oggetto Java nella memoria del runtime Android. Ovvero, il GC è un agente di raccolta, non in movimento, conservativo e distribuisce riferimenti diretti a tali oggetti. Di conseguenza, dopo una sequenza +g+, +w+, -g-, +g+, -w- il gref risultante avrà lo stesso valore del valore gref originale. In questo modo, l'avidità dei log risulta piuttosto semplice.

Android 4.0, tuttavia, dispone di un agente di raccolta mobile e non distribuisce più riferimenti diretti agli oggetti vm di runtime Android. Di conseguenza, dopo una sequenza +g+, +w+, -g-, +g+, -w- il valore gref sarà diverso. Se l'oggetto sopravvive a più controller di dominio, passerà da diversi valori gref, rendendo più difficile determinare da dove un'istanza è stata effettivamente allocata.

Esecuzione di query a livello di codice

È possibile eseguire query sui conteggi GREF e WREF eseguendo una query sull'oggetto JniRuntime .

Java.Interop.JniRuntime.CurrentRuntime.GlobalReferenceCount - Conteggio riferimenti globali

Java.Interop.JniRuntime.CurrentRuntime.WeakGlobalReferenceCount - Conteggio riferimenti deboli

Log di debug Android

I log di debug android possono fornire un contesto aggiuntivo per eventuali errori di runtime visualizzati.

Le prestazioni a virgola mobile sono terribili!

In alternativa, "L'app viene eseguita 10 volte più velocemente con la build di debug rispetto alla build di rilascio!"

Xamarin.Android supporta più ISTANZE del dispositivo: armeabi, armeabi-v7a e x86. È possibile specificare le interfacce di disponibilità dei dispositivi all'interno della scheda Applicazione proprietà > progetto Architetture supportate>.

Le compilazioni di debug usano un pacchetto Android che fornisce tutte le ISTANZE di archiviazione e quindi userà l'ABI più veloce per il dispositivo di destinazione.

Le build di rilascio includeranno solo le istanze di interruzione selezionate nella scheda Proprietà progetto. È possibile selezionare più di una.

armeabi è l'ABI predefinito e offre il supporto più ampio per i dispositivi. Armeabi, tuttavia, non supporta dispositivi multi-CPU e hardware a virgola mobile, altre cose. Di conseguenza, le app che usano il runtime armeabi Release saranno associate a un singolo core e usano un'implementazione soft-float. Entrambi questi possono contribuire a migliorare significativamente le prestazioni per la tua app.

Se la tua app richiede prestazioni decenti a virgola mobile (ad esempio giochi), devi abilitare l'ABI armeabi-v7a . Puoi voler supportare solo il runtime armeabi-v7a , anche se questo significa che i dispositivi meno recenti che supportano solo armeabi non potranno eseguire l'app.

Impossibile individuare Android SDK

Sono disponibili 2 download da Google per Android SDK per Windows. Se si sceglie il programma di installazione .exe, scriverà le chiavi del Registro di sistema che indicano a Xamarin.Android dove è stato installato. Se si sceglie il file .zip e lo si decomprime manualmente, Xamarin.Android non sa dove cercare l'SDK. È possibile indicare a Xamarin.Android dove si trova l'SDK in Visual Studio passando a Strumenti > Opzioni > Xamarin > Android Impostazioni:

Android SDK Location in Xamarin Android Settings

L'IDE non visualizza il dispositivo di destinazione

In alcuni casi si tenterà di distribuire l'applicazione in un dispositivo, ma il dispositivo in cui si vuole eseguire la distribuzione non viene visualizzato nella finestra di dialogo Seleziona dispositivo. Ciò può verificarsi quando Android Debug Bridge decide di andare in vacanza.

Per diagnosticare questo problema, trovare il programma adb, quindi eseguire:

adb devices

Se il dispositivo non è presente, è necessario riavviare il server Android Debug Bridge in modo che il dispositivo sia disponibile:

adb kill-server
adb start-server

Il software HTC Sync può impedire il corretto funzionamento del server di avvio adb. Se il comando adb start-server non stampa la porta su cui si avvia, chiudere il software HTC Sync e provare a riavviare il server adb.

Impossibile eseguire l'eseguibile dell'attività specificato "keytool"

Ciò significa che path non contiene la directory in cui si trova la directory bin di Java SDK. Verificare di aver seguito questi passaggi dalla Guida all'installazione.

monodroid.exe o aresgen.exe usciti con il codice 1

Per semplificare il debug di questo problema, passare a Visual Studio e modificare il livello di dettaglio di MSBuild, per eseguire questa operazione, selezionare: Strumenti > Opzioni > Progetto e soluzioni > Compila ed esegui > output compilazione progetto MSBuild Verbosity e impostare questo valore su Normale.

Ricompilare e controllare il riquadro Output di Visual Studio, che deve contenere l'errore completo.

Spazio di archiviazione insufficiente nel dispositivo per distribuire il pacchetto

Ciò si verifica quando non si avvia l'emulatore da Visual Studio. Quando si avvia l'emulatore all'esterno di Visual Studio, è necessario passare le -partition-size 512 opzioni, ad esempio

emulator -partition-size 512 -avd MonoDroid

Assicurarsi di usare il nome del simulatore corretto, ad esempio il nome usato durante la configurazione del simulatore.

INSTALL_FAILED_INVALID_APK durante l'installazione di un pacchetto

I nomi dei pacchetti Android devono contenere un punto ('.'). Modificare il nome del pacchetto in modo che contenga un punto.

  • In Visual Studio:
    • Fare clic con il pulsante destro del mouse su Proprietà del progetto >
    • Fare clic sulla scheda Manifesto Android a sinistra.
    • Aggiornare il campo Nome pacchetto.
      • Se viene visualizzato il messaggio "Nessun AndroidManifest.xml trovato. Fare clic per aggiungere one.", fare clic sul collegamento e quindi aggiornare il campo Nome pacchetto.
  • All'interno di Visual Studio per Mac:
    • Fare clic con il pulsante destro del mouse sul progetto > Opzioni.
    • Passare alla sezione Build/Android Application (Compila/Applicazione Android).
    • Modificare il campo Nome pacchetto in modo che contenga un '.'.

INSTALL_FAILED_MISSING_SHARED_LIBRARY durante l'installazione di un pacchetto

Una "libreria condivisa" in questo contesto non è un file di libreria condivisa nativa (libfoo.so), ma è invece una libreria che deve essere installata separatamente nel dispositivo di destinazione, ad esempio Google Mappe.

Il pacchetto Android specifica le librerie condivise necessarie con l'elemento <uses-library/> . Se una libreria richiesta non è presente nel dispositivo di destinazione (ad esempio //uses-library/@android:required , è true, ovvero l'impostazione predefinita), l'installazione del pacchetto avrà esito negativo con INSTALL_FAILED_MISSING_SHARED_LIBRARY.

Per determinare quali librerie condivise sono necessarie, visualizzare il file di AndroidManifest.xml generato(ad esempio obj\Debug\android\AndroidManifest.xml) e cercare gli <uses-library/> elementi. <uses-library/>Gli elementi possono essere aggiunti manualmente nel file Properties\AndroidManifest.xml del progetto e tramite l'attributo personalizzato UsesLibraryAttribute.

Ad esempio, l'aggiunta di un riferimento all'assembly a Mono.Android.Google Mappe.dll aggiungerà in modo implicito un oggetto <uses-library/> per la libreria condivisa di Google Mappe.

INSTALL_FAILED_UPDATE_INCOMPATIBLE durante l'installazione di un pacchetto

I pacchetti Android hanno tre requisiti:

  • Devono contenere un '.' (vedere la voce precedente)
  • Devono avere un nome di pacchetto stringa univoco (quindi la convenzione reverse-tld visualizzata nei nomi delle app Android, e.g. com.android.chrome per l'app Chrome)
  • Quando si aggiornano i pacchetti, il pacchetto deve avere la stessa chiave di firma.

Di conseguenza, si immagini questo scenario:

  1. Si compila e distribuisce l'app come app di debug
  2. Si modifica la chiave di firma, ad esempio per l'uso come app release (o perché non si preferisce la chiave di firma di debug fornita per impostazione predefinita)
  3. Installare l'app senza rimuoverla per prima, ad esempio Avvia debug > senza eseguire debug all'interno di Visual Studio

In questo caso, l'installazione del pacchetto avrà esito negativo con un errore di INSTALL_FAILED_UPDATE_INCOMPATIBLE, perché il nome del pacchetto non è stato modificato mentre la chiave di firma è stata eseguita. Il log di debug Android conterrà anche un messaggio simile al seguente:

E/PackageManager(  146): Package [PackageName] signatures do not match the previously installed version; ignoring!

Per correggere questo errore, rimuovere completamente l'applicazione dal dispositivo prima di reinstallare.

INSTALL_FAILED_UID_CHANGED durante l'installazione di un pacchetto

Quando viene installato un pacchetto Android, viene assegnato un ID utente (UID). In alcuni casi, per motivi attualmente sconosciuti, durante l'installazione su un'app già installata, l'installazione avrà esito negativo con INSTALL_FAILED_UID_CHANGED:

ERROR [2015-03-23 11:19:01Z]: ANDROID: Deployment failed
Mono.AndroidTools.InstallFailedException: Failure [INSTALL_FAILED_UID_CHANGED]
   at Mono.AndroidTools.Internal.AdbOutputParsing.CheckInstallSuccess(String output, String packageName)
   at Mono.AndroidTools.AndroidDevice.<>c__DisplayClass2c.<InstallPackage>b__2b(Task`1 t)
   at System.Threading.Tasks.ContinuationTaskFromResultTask`1.InnerInvoke()
   at System.Threading.Tasks.Task.Execute()

Per risolvere questo problema, disinstallare completamente il pacchetto Android installando l'app dall'interfaccia utente grafica della destinazione Android o usando adb:

$ adb uninstall @PACKAGE_NAME@

NON U edizione Standardadb uninstall -k, in quanto mantiene i dati dell'applicazione e mantiene quindi l'UID in conflitto nel dispositivo di destinazione.

L'avvio delle app di rilascio non riesce nel dispositivo

L'output del log di debug Android conterrà un messaggio simile al seguente:

D/AndroidRuntime( 1710): Shutting down VM
W/dalvikvm( 1710): threadid=1: thread exiting with uncaught exception (group=0xb412f180)
E/AndroidRuntime( 1710): FATAL EXCEPTION: main
E/AndroidRuntime( 1710): java.lang.UnsatisfiedLinkError: Couldn't load monodroid: findLibrary returned null
E/AndroidRuntime( 1710):        at java.lang.Runtime.loadLibrary(Runtime.java:365)

In questo caso, esistono due possibili cause:

  1. Il .apk non fornisce un'interfaccia ABI supportata dal dispositivo di destinazione. Ad esempio, il .apk contiene solo file binari armeabi-v7a e il dispositivo di destinazione supporta solo armeabi.

  2. Bug di Android. In questo caso, disinstallare l'app, passare le dita e reinstallare l'app.

Per correggere (1), modificare le opzioni/proprietà del progetto e aggiungere il supporto per l'interfaccia ABI necessaria all'elenco delle interfacce abi supportate. Per determinare quale ABI è necessario aggiungere, eseguire il comando adb seguente sul dispositivo di destinazione:

adb shell getprop ro.product.cpu.abi
adb shell getprop ro.product.cpu.abi2

L'output conterrà le API primarie (e secondarie facoltative).

$ adb shell getprop | grep ro.product.cpu
[ro.product.cpu.abi2]: [armeabi]
[ro.product.cpu.abi]: [armeabi-v7a]

La proprietà OutPath non è impostata per il progetto "MyApp.csproj"

Questo significa in genere che si dispone di un computer HP e la variabile di ambiente "Platform" è stata impostata su qualcosa come MCD o HPD. Questo è in conflitto con la proprietà MSBuild Platform impostata in genere su "Qualsiasi CPU" o "x86". È necessario rimuovere questa variabile di ambiente dal computer prima che MSBuild funzioni:

  • > variabili di ambiente avanzate > del sistema > Pannello di controllo

Riavviare Visual Studio o Visual Studio per Mac e provare a ricompilare. Le cose dovrebbero ora funzionare come previsto.

java.lang.ClassCastException: mono.android.runtime.JavaObject non può essere eseguito il cast in...

Xamarin.Android 4.x non esegue correttamente il marshalling dei tipi generici annidati correttamente. Si consideri ad esempio il codice C# seguente usando SimpleExpandableListAdapter:

// BAD CODE; DO NOT USE
var groupData = new List<IDictionary<string, object>> () {
        new Dictionary<string, object> {
                { "NAME", "Group 1" },
                { "IS_EVEN", "This group is odd" },
        },
};
var childData = new List<IList<IDictionary<string, object>>> () {
        new List<IDictionary<string, object>> {
                new Dictionary<string, object> {
                        { "NAME", "Child 1" },
                        { "IS_EVEN", "This group is odd" },
                },
        },
};
mAdapter = new SimpleExpandableListAdapter (
        this,
        groupData,
        Android.Resource.Layout.SimpleExpandableListItem1,
        new string[] { "NAME", "IS_EVEN" },
        new int[] { Android.Resource.Id.Text1, Android.Resource.Id.Text2 },
        childData,
        Android.Resource.Layout.SimpleExpandableListItem2,
        new string[] { "NAME", "IS_EVEN" },
        new int[] { Android.Resource.Id.Text1, Android.Resource.Id.Text2 }
);

Il problema è che Xamarin.Android esegue erroneamente il marshalling dei tipi generici annidati. l'oggetto List<IDictionary<string, object>> viene sottoposto a marshalling a java.lang.ArrrayList, ma ArrayList contiene istanze (mono.android.runtime.JavaObjectche fanno riferimento alle Dictionary<string, object> istanze) invece di un elemento che implementa java.util.Map, generando l'eccezione seguente:

E/AndroidRuntime( 2991): FATAL EXCEPTION: main
E/AndroidRuntime( 2991): java.lang.ClassCastException: mono.android.runtime.JavaObject cannot be cast to java.util.Map
E/AndroidRuntime( 2991):        at android.widget.SimpleExpandableListAdapter.getGroupView(SimpleExpandableListAdapter.java:278)
E/AndroidRuntime( 2991):        at android.widget.ExpandableListConnector.getView(ExpandableListConnector.java:446)
E/AndroidRuntime( 2991):        at android.widget.AbsListView.obtainView(AbsListView.java:2271)
E/AndroidRuntime( 2991):        at android.widget.ListView.makeAndAddView(ListView.java:1769)
E/AndroidRuntime( 2991):        at android.widget.ListView.fillDown(ListView.java:672)
E/AndroidRuntime( 2991):        at android.widget.ListView.fillFromTop(ListView.java:733)
E/AndroidRuntime( 2991):        at android.widget.ListView.layoutChildren(ListView.java:1622)

La soluzione alternativa consiste nell'usare i tipi di raccolta Java forniti anziché i tipi per i System.Collections.Generic tipi "interni". Ciò comporterà i tipi Java appropriati durante il marshalling delle istanze. Il codice seguente è più complesso del necessario per ridurre la durata di gref. Può essere semplificato per modificare il codice originale tramite s/List/JavaList/g e s/Dictionary/JavaDictionary/g se le durate gref non sono un problema.

// insert good code here
using (var groupData = new JavaList<IDictionary<string, object>> ()) {
    using (var groupEntry = new JavaDictionary<string, object> ()) {
        groupEntry.Add ("NAME", "Group 1");
        groupEntry.Add ("IS_EVEN", "This group is odd");
        groupData.Add (groupEntry);
    }
    using (var childData = new JavaList<IList<IDictionary<string, object>>> ()) {
        using (var childEntry = new JavaList<IDictionary<string, object>> ())
        using (var childEntryDict = new JavaDictionary<string, object> ()) {
            childEntryDict.Add ("NAME", "Child 1");
            childEntryDict.Add ("IS_EVEN", "This child is odd.");
            childEntry.Add (childEntryDict);
            childData.Add (childEntry);
        }
        mAdapter = new SimpleExpandableListAdapter (
            this,
            groupData,
            Android.Resource.Layout.SimpleExpandableListItem1,
            new string[] { "NAME", "IS_EVEN" },
            new int[] { Android.Resource.Id.Text1, Android.Resource.Id.Text2 },
            childData,
            Android.Resource.Layout.SimpleExpandableListItem2,
            new string[] { "NAME", "IS_EVEN" },
            new int[] { Android.Resource.Id.Text1, Android.Resource.Id.Text2 }
        );
    }
}

Valori NullReferenceExceptions imprevisti

In alcuni casi, il log di debug Android menziona NullReferenceExceptions che "non può accadere" o proviene dal codice di runtime mono per Android poco prima che l'app muora:

E/mono(15202): Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object
E/mono(15202):   at Java.Lang.Object.GetObject (IntPtr handle, System.Type type, Boolean owned)
E/mono(15202):   at Java.Lang.Object._GetObject[IOnTouchListener] (IntPtr handle, Boolean owned)
E/mono(15202):   at Java.Lang.Object.GetObject[IOnTouchListener] (IntPtr handle, Boolean owned)
E/mono(15202):   at Android.Views.View+IOnTouchListenerAdapter.n_OnTouch_Landroid_view_View_Landroid_view_MotionEvent_(IntPtr jnienv, IntPtr native__this, IntPtr native_v, IntPtr native_e)
E/mono(15202):   at (wrapper dynamic-method) object:b039cbb0-15e9-4f47-87ce-442060701362 (intptr,intptr,intptr,intptr)

or

E/mono    ( 4176): Unhandled Exception:
E/mono    ( 4176): System.NullReferenceException: Object reference not set to an instance of an object
E/mono    ( 4176): at Android.Runtime.JNIEnv.NewString (string)
E/mono    ( 4176): at Android.Util.Log.Info (string,string)

Ciò può verificarsi quando il runtime Android decide di interrompere il processo, che può verificarsi per qualsiasi numero di motivi, tra cui il raggiungimento del limite GREF di destinazione o l'esecuzione di qualcosa di "errato" con JNI.

Per verificare se questo è il caso, controllare il log di debug Android per un messaggio del processo simile al seguente:

E/dalvikvm(  123): VM aborting

Interruzione a causa dell'esaurimento dei riferimenti globali

Il livello JNI del runtime Android supporta solo un numero limitato di riferimenti all'oggetto JNI per essere validi in qualsiasi momento specifico. Quando questo limite viene superato, le operazioni si interrompono.

Il limite GREF (riferimento globale) è 2000 riferimenti nell'emulatore e circa 52000 riferimenti sull'hardware.

Si sa che si sta iniziando a creare troppe gref quando vengono visualizzati messaggi come questo nel log di debug Android:

D/dalvikvm(  602): GREF has increased to 1801

Quando si raggiunge il limite GREF, viene stampato un messaggio simile al seguente:

D/dalvikvm(  602): GREF has increased to 2001
W/dalvikvm(  602): Last 10 entries in JNI global reference table:
W/dalvikvm(  602):  1991: 0x4057eff8 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm(  602):  1992: 0x4057f010 cls=Landroid/graphics/Point; (28 bytes)
W/dalvikvm(  602):  1993: 0x40698e70 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm(  602):  1994: 0x40698e88 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm(  602):  1995: 0x40698ea0 cls=Landroid/graphics/Point; (28 bytes)
W/dalvikvm(  602):  1996: 0x406981f0 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm(  602):  1997: 0x40698208 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm(  602):  1998: 0x40698220 cls=Landroid/graphics/Point; (28 bytes)
W/dalvikvm(  602):  1999: 0x406956a8 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm(  602):  2000: 0x406956c0 cls=Landroid/graphics/Point; (20 bytes)
W/dalvikvm(  602): JNI global reference table summary (2001 entries):
W/dalvikvm(  602):    51 of Ljava/lang/Class; 164B (41 unique)
W/dalvikvm(  602):    46 of Ljava/lang/Class; 188B (17 unique)
W/dalvikvm(  602):     6 of Ljava/lang/Class; 212B (6 unique)
W/dalvikvm(  602):    11 of Ljava/lang/Class; 236B (7 unique)
W/dalvikvm(  602):     3 of Ljava/lang/Class; 260B (3 unique)
W/dalvikvm(  602):     4 of Ljava/lang/Class; 284B (2 unique)
W/dalvikvm(  602):     8 of Ljava/lang/Class; 308B (6 unique)
W/dalvikvm(  602):     1 of Ljava/lang/Class; 316B
W/dalvikvm(  602):     4 of Ljava/lang/Class; 332B (3 unique)
W/dalvikvm(  602):     1 of Ljava/lang/Class; 356B
W/dalvikvm(  602):     2 of Ljava/lang/Class; 380B (1 unique)
W/dalvikvm(  602):     1 of Ljava/lang/Class; 428B
W/dalvikvm(  602):     1 of Ljava/lang/Class; 452B
W/dalvikvm(  602):     1 of Ljava/lang/Class; 476B
W/dalvikvm(  602):     2 of Ljava/lang/Class; 500B (1 unique)
W/dalvikvm(  602):     1 of Ljava/lang/Class; 548B
W/dalvikvm(  602):     1 of Ljava/lang/Class; 572B
W/dalvikvm(  602):     2 of Ljava/lang/Class; 596B (2 unique)
W/dalvikvm(  602):     1 of Ljava/lang/Class; 692B
W/dalvikvm(  602):     1 of Ljava/lang/Class; 956B
W/dalvikvm(  602):     1 of Ljava/lang/Class; 1004B
W/dalvikvm(  602):     1 of Ljava/lang/Class; 1148B
W/dalvikvm(  602):     2 of Ljava/lang/Class; 1172B (1 unique)
W/dalvikvm(  602):     1 of Ljava/lang/Class; 1316B
W/dalvikvm(  602):     1 of Ljava/lang/Class; 3428B
W/dalvikvm(  602):     1 of Ljava/lang/Class; 3452B
W/dalvikvm(  602):     1 of Ljava/lang/String; 28B
W/dalvikvm(  602):     2 of Ldalvik/system/VMRuntime; 12B (1 unique)
W/dalvikvm(  602):    10 of Ljava/lang/ref/WeakReference; 28B (10 unique)
W/dalvikvm(  602):     1 of Ldalvik/system/PathClassLoader; 44B
W/dalvikvm(  602):  1553 of Landroid/graphics/Point; 20B (1553 unique)
W/dalvikvm(  602):   261 of Landroid/graphics/Point; 28B (261 unique)
W/dalvikvm(  602):     1 of Landroid/view/MotionEvent; 100B
W/dalvikvm(  602):     1 of Landroid/app/ActivityThread$ApplicationThread; 28B
W/dalvikvm(  602):     1 of Landroid/content/ContentProvider$Transport; 28B
W/dalvikvm(  602):     1 of Landroid/view/Surface$CompatibleCanvas; 44B
W/dalvikvm(  602):     1 of Landroid/view/inputmethod/InputMethodManager$ControlledInputConnectionWrapper; 36B
W/dalvikvm(  602):     1 of Landroid/view/ViewRoot$1; 12B
W/dalvikvm(  602):     1 of Landroid/view/ViewRoot$W; 28B
W/dalvikvm(  602):     1 of Landroid/view/inputmethod/InputMethodManager$1; 28B
W/dalvikvm(  602):     1 of Landroid/view/accessibility/AccessibilityManager$1; 28B
W/dalvikvm(  602):     1 of Landroid/widget/LinearLayout$LayoutParams; 44B
W/dalvikvm(  602):     1 of Landroid/widget/LinearLayout; 332B
W/dalvikvm(  602):     2 of Lorg/apache/harmony/xnet/provider/jsse/TrustManagerImpl; 28B (1 unique)
W/dalvikvm(  602):     1 of Landroid/view/SurfaceView$MyWindow; 36B
W/dalvikvm(  602):     1 of Ltouchtest/RenderThread; 92B
W/dalvikvm(  602):     1 of Landroid/view/SurfaceView$3; 12B
W/dalvikvm(  602):     1 of Ltouchtest/DrawingView; 412B
W/dalvikvm(  602):     1 of Ltouchtest/Activity1; 180B
W/dalvikvm(  602): Memory held directly by tracked refs is 75624 bytes
E/dalvikvm(  602): Excessive JNI global references (2001)
E/dalvikvm(  602): VM aborting

Nell'esempio precedente (che, per inciso, deriva dal bug 685215) il problema è che vengono create troppe istanze di Android.Graphics.Point. Per un elenco delle correzioni per questo particolare bug, vedere commento 2 .

In genere, una soluzione utile consiste nel trovare il tipo con troppe istanze allocate, Android.Graphics.Point nel dump precedente, quindi individuare dove vengono create nel codice sorgente ed eliminarle in modo appropriato (in modo che la durata dell'oggetto Java venga abbreviata). Questo non è sempre appropriato (#685215 è multithreading, quindi la soluzione semplice evita la chiamata Dispose), ma è la prima cosa da considerare.

È possibile abilitare la registrazione GREF per verificare quando vengono create le funzioni GREF e quante esistono.

Interruzione a causa della mancata corrispondenza del tipo JNI

Se si esegue il rollback del codice JNI, è possibile che i tipi non corrispondano correttamente, ad esempio se si tenta di richiamare java.lang.Runnable.run su un tipo che non implementa java.lang.Runnable. In questo caso, verrà visualizzato un messaggio simile al seguente nel log di debug android:

W/dalvikvm( 123): JNI WARNING: can't call Ljava/Type;;.method on instance of Lanother/java/Type;
W/dalvikvm( 123):              in Lmono/java/lang/RunnableImplementor;.n_run:()V (CallVoidMethodA)
...
E/dalvikvm( 123): VM aborting

Supporto del codice dinamico

Il codice dinamico non viene compilato

Per usare C# dinamica nell'applicazione o nella libreria, è necessario aggiungere System.Core.dll, Microsoft.CSharp.dll e Mono.CSharp.dll al progetto.

Nella build di rilascio, MissingMethodException si verifica per il codice dinamico in fase di esecuzione.

  • È probabile che il progetto dell'applicazione non abbia riferimenti a System.Core.dll, Microsoft.CSharp.dll o Mono.CSharp.dll. Assicurarsi che a tali assembly venga fatto riferimento.

    • Tenere presente che il codice dinamico costa sempre. Se è necessario codice efficiente, è consigliabile non usare il codice dinamico.
  • Nella prima anteprima, tali assembly sono stati esclusi a meno che i tipi in ogni assembly non vengano usati in modo esplicito dal codice dell'applicazione. Per una soluzione alternativa, vedere quanto segue: http://lists.ximian.com/pipermail/mo...il/009798.html

Progetti creati con arresto anomalo di AOT+LLVM nei dispositivi x86

Quando si distribuisce un'app compilata con AOT+LLVM nei dispositivi basati su x86, è possibile che venga visualizzato un messaggio di errore di eccezione simile al seguente:

Assertion: should not be reached at /Users/.../external/mono/mono/mini/tramp-x86.c:124
Fatal signal 6 (SIGABRT), code -6 in tid 4051 (Xamarin.bug56111)

Si tratta di un problema noto: la soluzione alternativa consiste nel disabilitare LLVM.