Verifica del comportamento delle app sul runtime Android (ART)

Il runtime Android (ART) è il runtime predefinito per i dispositivi che eseguono Android 5.0 (livello API 21) e versioni successive. Questo runtime offre una serie di funzionalità che migliorano le prestazioni e la fluidità della piattaforma e delle app Android. Puoi trovare ulteriori informazioni sulle nuove funzionalità di ART nella sezione Introduzione ARTE.

Tuttavia, alcune tecniche che funzionano su Dalvik non funzionano su ART. Questo di questo documento consente di sapere cosa occorre tenere presente quando si esegue la migrazione di un compatibile con ART. La maggior parte delle app dovrebbe funzionare solo con ART.

Risolvere i problemi di garbage collection (GC)

In Dalvik, le app spesso trovano utile chiamare in modo esplicito System.gc() per richiedere la garbage collection (GC). Dovrebbe essere molto meno necessario con ART, in particolare se si richiama la garbage collection per impedire le app di tipo GC_FOR_ALLOC o per ridurre la frammentazione. Puoi verificare quale runtime è in uso chiamando il numero System.getProperty("java.vm.version"). Se ART è in uso, il valore della proprietà è "2.0.0" o superiore.

ART utilizza il raccoglitore di copia contemporanea (CC) che compatta contemporaneamente l'heap Java. Per questo motivo, evita di usare tecniche incompatibili con la compattazione di GC (come il salvataggio dei cursori dell'istanza di servizio). Questo è particolarmente importante per le app che utilizzano JNI (Java Native Interface). Per ulteriori informazioni, consulta l'articolo sulla prevenzione dei problemi JNI.

Prevenire i problemi di JNI

Il JNI di ART è un po' più severo di quello di Dalvik. È un'idea particolarmente buona per usare la modalità CheckJNI per rilevare problemi comuni. Se la tua app utilizza C/C++ consulta il seguente articolo:

Debug Android JNI con CheckJNI

Controllo del codice JNI per problemi di garbage collection

Il raccoglitore per la copia simultanea (CC) potrebbe spostare gli oggetti in memoria per la compattazione. Se utilizzi il codice C/C++, eseguire operazioni incompatibili con la compattazione di GC. Abbiamo migliorato ControllaJNI per identificare alcuni potenziali problemi (come descritto in JNI modifiche nei riferimenti locali in ICS).

Un aspetto da considerare in particolare è l'uso Get...ArrayElements() e Release...ArrayElements() funzioni. Nei runtime con GC non compatta, Le funzioni Get...ArrayElements() in genere restituiscono un riferimento alla effettiva a supporto dell'oggetto array. Se apporti una modifica a una delle restituiti elementi array, l'oggetto array è a sua volta modificato (e gli argomenti a Release...ArrayElements() vengono generalmente ignorati). Tuttavia, se la compattazione di GC è in uso, le funzioni Get...ArrayElements() potrebbero e restituire una copia della memoria. Se si usa il riferimento in modo improprio durante la compattazione di GC durante l'uso, questo può causare il danneggiamento della memoria o altri problemi. Ad esempio:

  • Se apporti modifiche agli elementi array restituiti, devi richiamare il metodo la funzione Release...ArrayElements() appropriata quando hai finito, per assicurarti che le modifiche apportate vengano copiate correttamente .
  • Quando rilasci gli elementi array di memoria, devi utilizzare la classe in base alle modifiche apportate:
    • Se non hai apportato modifiche agli elementi dell'array, utilizza Modalità JNI_ABORT, che rilascia la memoria senza copiare all'oggetto array sottostante.
    • Se hai apportato modifiche all'array e non è necessario il riferimento utilizza il codice 0 (che aggiorna l'oggetto array e libera la copia della memoria).
    • Se hai apportato modifiche all'array di cui vuoi eseguire il commit e vuoi per conservare la copia dell'array, utilizza JNI_COMMIT (che si aggiorna l'oggetto array sottostante e conserva la copia).
  • Quando chiami Release...ArrayElements(), restituisci lo stesso che è stato originariamente restituito da Get...ArrayElements(). Per Ad esempio, non è sicuro incrementare il puntatore originale (per eseguire la scansione restituiti elementi dell'array), quindi passa il puntatore incrementato Release...ArrayElements(). Il superamento di questo puntatore modificato può causare la memoria sbagliata da liberare, con un conseguente danneggiamento della memoria.

Gestione degli errori

JNI di ART genera errori in diversi casi in cui Dalvik non lo fa. (Una volta puoi rilevare molti casi di questo tipo eseguendo test con CheckJNI.)

Ad esempio, se RegisterNatives viene chiamato con un metodo che non esiste (forse perché il metodo è stato rimosso da uno strumento come ProGuard), ART ora genera correttamente NoSuchMethodError:

08-12 17:09:41.082 13823 13823 E AndroidRuntime: FATAL EXCEPTION: main
08-12 17:09:41.082 13823 13823 E AndroidRuntime: java.lang.NoSuchMethodError:
    no static or non-static method
    "Lcom/foo/Bar;.native_frob(Ljava/lang/String;)I"
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.nativeLoad(Native Method)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.doLoad(Runtime.java:421)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.Runtime.loadLibrary(Runtime.java:362)
08-12 17:09:41.082 13823 13823 E AndroidRuntime:
    at java.lang.System.loadLibrary(System.java:526)

ART registra anche un errore (visibile in logcat) se RegisterNatives è chiamata senza alcun metodo:

W/art     ( 1234): JNI RegisterNativeMethods: attempt to register 0 native
methods for <classname>

Inoltre, JNI funziona GetFieldID() e GetStaticFieldID() ora lancia correttamente NoSuchFieldError anziché restituire semplicemente null. Analogamente, GetMethodID() e GetStaticMethodID() ora lancia correttamente NoSuchMethodError. Ciò può causare errori CheckJNI a causa di eccezioni non gestite o del per i chiamanti Java del codice nativo. Ciò rende particolarmente importante per testare le app compatibili con ART con la modalità CheckJNI.

ART si aspetta che gli utenti utilizzino i metodi CallNonvirtual...Method() di JNI (ad esempio CallNonvirtualVoidMethod()) per utilizzare la dichiarazione del metodo non è una sottoclasse, come richiesto dalla specifica JNI.

Prevenire i problemi relativi alle dimensioni dello stack

Dalvik aveva stack separati per il codice nativo e Java, con un'architettura Java predefinita una dimensione dello stack di 32 kB e uno stack nativo predefinito di 1 MB. ART ha un obiettivo per una località migliore. Solitamente, lo stack Thread ART dovrebbe essere più o meno uguale a quella di Dalvik. Tuttavia, se specifichi esplicitamente e impostare le dimensioni dello stack, potresti dover rivedere questi valori per le app in esecuzione ART.

  • In Java, rivedi le chiamate al costruttore Thread che specificano uno stack esplicito dimensioni. Ad esempio, dovrai aumentare le dimensioni se si verifica il StackOverflowError.
  • In C/C++, esamina l'utilizzo di pthread_attr_setstack() e pthread_attr_setstacksize() per i thread che eseguono anche il codice Java tramite JNI. Ecco un esempio di errore registrato quando un'app tenta di chiamare JNI AttachCurrentThread() quando la dimensione del thread p è troppo piccola:
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

Modifiche al modello a oggetti

Dalvik ha consentito erroneamente alle sottoclassi di sostituire i metodi privati del pacchetto. ART genera un avviso nei seguenti casi:

Before Android 4.1, method void com.foo.Bar.quux()
would have incorrectly overridden the package-private method in
com.quux.Quux

Se intendi eseguire l'override del metodo di una classe in un pacchetto diverso, dichiara la come public o protected.

Ora Object ha campi privati. App che si riflettono sui campi nelle loro gerarchie di classi, dobbiamo fare attenzione a non cercare di esaminare campi di Object. Ad esempio, se stai ripetendo un corso la gerarchia come parte di un framework di serializzazione,

Class.getSuperclass() == java.lang.Object.class

anziché continuare finché il metodo non restituisce null.

Il proxy InvocationHandler.invoke() ora riceve null se non sono presenti anziché un array vuoto. Questo comportamento è stato documentato in precedenza, ma non vengono gestite correttamente in Dalvik. Le versioni precedenti di Mockito hanno difficoltà con questo, quindi utilizza una versione Mockito aggiornata durante il test con ART.

Risoluzione dei problemi di compilazione AOT

La compilazione Java Ahead-Of-Time (AOT) di ART dovrebbe funzionare per tutte le applicazioni Java standard le API nel tuo codice. Compilation eseguita da ART Strumento dex2oat; in caso di problemi relativi dex2oat al momento dell'installazione, comunicacelo (consulta la sezione Segnalazione di problemi) per consentirci di risolverli il più rapidamente possibile. il più possibile. Alcuni aspetti da sottolineare:

  • Al momento dell'installazione, ART esegue una verifica bytecode più rigorosa rispetto a Dalvik. Il codice prodotto dagli strumenti di compilazione di Android dovrebbe essere sufficiente. Tuttavia, alcune strumenti di post-elaborazione (soprattutto gli strumenti che eseguono l'offuscamento) possono generare file non validi che sono tollerati da Dalvik ma rifiutati da ART. Siamo stati collaborare con i fornitori di strumenti per individuare e risolvere questi problemi. In molti casi, ottenere le versioni più recenti degli strumenti e la rigenerazione dei file DEX può per risolvere problemi di produzione e facilità d'uso.
  • Ecco alcuni problemi tipici segnalati dallo strumento di verifica ART:
    • flusso di controllo non valido
    • sbilanciato: monitorenter/monitorexit
    • Dimensioni elenco dei tipi di parametri di lunghezza 0
  • Alcune app hanno dipendenze nel file .odex installato formato in /system/framework, /data/dalvik-cache o nella directory di output ottimizzata di DexClassLoader. Questi ora sono file ELF e non una forma estesa di file DEX. Mentre ART prova seguire le stesse regole di denominazione e blocco di Dalvik, le app non sul formato file; il formato è soggetto a modifiche senza preavviso.

    Nota: in Android 8.0 (livello API 26) e superiore, la directory di output ottimizzata per DexClassLoader è stata ritirata. Per ulteriori informazioni, consulta la documentazione relativa DexClassLoader() come costruttore.

Problemi relativi ai report

Se riscontri problemi non dovuti a problemi relativi a JNI dell'app, segnala tramite lo strumento Android Open Source Issue Tracker all'indirizzo https://code.google.com/p/android/issues/list. Includi un "adb bugreport" e un link all'app nella sezione Play Store, se disponibile. Altrimenti, se possibile, allega un APK che riproduca risolvere il problema. Tieni presente che i problemi (inclusi gli allegati) sono visibili pubblicamente visibile.