Android Runtime (ART) è il runtime predefinito per i dispositivi con 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 ad ART.
Tuttavia, alcune tecniche che funzionano su Dalvik non funzionano con l'ART. Questo documento illustra gli aspetti da considerare quando si esegue la migrazione di un'app esistente in modo che sia 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 esplicitamente System.gc()
per richiedere la garbage collection (GC). Questa operazione dovrebbe essere
molto meno necessaria con ART, in particolare se stai richiamando la garbage collection
per prevenire occorrenze di tipo GC_FOR_ALLOC
o per ridurre la frammentazione. Puoi verificare quale runtime è in uso chiamando System.getProperty("java.vm.version")
. Se ART è in uso, il valore della proprietà
è pari o superiore a "2.0.0"
.
ART utilizza il raccoglitore CC (Concurrent Copia) che compatta contemporaneamente l'heap Java. Per questo motivo, evita di utilizzare tecniche incompatibili con la compattazione di GC (come il salvataggio dei puntatori ai dati delle istanze di oggetti). Ciò è particolarmente importante per le app che utilizzano Java Native Interface (JNI). Per ulteriori informazioni, consulta la sezione Prevenzione dei problemi JNI.
Prevenzione dei problemi JNI
JNI di ART è un po' più rigoroso di quello di Dalvik. È consigliabile usare la modalità CheckJNI per individuare i problemi comuni. Se la tua app utilizza codice C/C++, consulta il seguente articolo:
Debug di Android JNI con CheckJNI
Controllo del codice JNI per problemi di garbage collection in corso...
Il raccoglitore Copia simultanea (CC) potrebbe spostare gli oggetti in memoria per la compattazione. Se utilizzi il codice C/C++, non eseguire operazioni incompatibili con la compattazione di GC. Abbiamo migliorato CheckJNI per identificare alcuni potenziali problemi (come descritto in Modifiche ai riferimenti locali di JNI in ICS).
Un aspetto da tenere d'occhio in particolare è l'uso delle funzioni Get...ArrayElements()
e Release...ArrayElements()
. Nei runtime con GC non compatta, le funzioni Get...ArrayElements()
in genere restituiscono un riferimento alla memoria effettiva che supporta l'oggetto array. Se apporti una modifica a uno degli
elementi array restituiti, l'oggetto array viene modificato (e gli argomenti
per Release...ArrayElements()
vengono generalmente ignorati). Tuttavia, se
il GC compatta è in uso, le funzioni Get...ArrayElements()
possono
restituire una copia della memoria. L'uso improprio del riferimento durante la compattazione di GC potrebbe danneggiare la memoria o causare altri problemi. Ecco alcuni esempi:
- Se apporti modifiche agli elementi dell'array restituiti, devi chiamare la funzione
Release...ArrayElements()
appropriata al termine dell'operazione per assicurarti che le modifiche apportate vengano copiate correttamente nell'oggetto array sottostante. - Quando rilasci gli elementi dell'array di memoria, devi utilizzare la modalità appropriata, a seconda delle modifiche apportate:
- Se non hai apportato modifiche agli elementi array, utilizza la modalità
JNI_ABORT
, che rilascia la memoria senza copiare le modifiche nell'oggetto array sottostante. - Se hai apportato modifiche all'array e non hai più bisogno del riferimento,
utilizza il codice
0
(che aggiorna l'oggetto array e libera la copia di memoria). - Se hai apportato modifiche all'array di cui vuoi eseguire il commit e vuoi conservare la copia dell'array, utilizza
JNI_COMMIT
(che aggiorna l'oggetto array sottostante e conserva la copia).
- Se non hai apportato modifiche agli elementi array, utilizza la modalità
- Quando chiami
Release...ArrayElements()
, restituisci lo stesso puntatore originariamente restituito daGet...ArrayElements()
. Ad esempio, non è sicuro incrementare il puntatore originale (per analizzare gli elementi dell'array restituiti) e poi passare il puntatore incrementato aRelease...ArrayElements()
. Se passi questo puntatore modificato, puoi liberare la memoria errata, danneggiando la memoria.
Gestione degli errori
La JNI di ART genera errori in una serie di casi in cui Dalvik non lo fa. Anche in questo caso, puoi rilevare molti di questi casi eseguendo un 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
viene chiamato senza metodi:
W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native methods for <classname>
Inoltre, le funzioni JNI GetFieldID()
e GetStaticFieldID()
ora generano correttamente NoSuchFieldError
anziché restituire semplicemente null. Allo stesso modo, GetMethodID()
e
GetStaticMethodID()
ora restituiscono correttamente NoSuchMethodError
.
Ciò può portare a errori CheckJNI a causa delle eccezioni non gestite o delle eccezioni che vengono generate per i chiamanti Java del codice nativo. Ciò rende
particolarmente importante testare app compatibili con ART con la modalità CheckJNI.
ART si aspetta che gli utenti dei metodi CallNonvirtual...Method()
JNI
(come CallNonvirtualVoidMethod()
) utilizzino la classe dichiarante del metodo, non una sottoclasse, come richiesto dalla specifica JNI.
Evitare i problemi relativi alle dimensioni dello stack
Dalvik aveva stack separati per codice nativo e Java, con una dimensione dello stack Java predefinita di 32 kB e una dimensione dello stack nativa predefinita di 1 MB. ART ha uno stack unificato
per una migliore località. In genere, le dimensioni dello stack Thread
ART
devono essere all'incirca le stesse di Dalvik. Tuttavia, se imposti esplicitamente le dimensioni dello stack, potresti dover rivedere questi valori per le app in esecuzione in ART.
- In Java, esamina le chiamate al costruttore
Thread
che specificano una dimensione esplicita dello stack. Ad esempio, dovrai aumentare le dimensioni se si verificaStackOverflowError
. - In C/C++, esamina l'utilizzo di
pthread_attr_setstack()
epthread_attr_setstacksize()
per i thread che eseguono anche codice Java tramite JNI. Ecco un esempio dell'errore registrato quando un'app tenta di chiamare JNIAttachCurrentThread()
quando la dimensione del file pthread è 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 erroneamente consentito alle sottoclassi di eseguire l'override dei metodi pacchetto-private. ART emette 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 altro pacchetto, dichiara il metodo come public
o protected
.
Object
ora dispone di campi privati. Le app che riflettono sui campi
nelle loro gerarchie di classi dovrebbero fare attenzione a non tentare di esaminare
i campi di Object
. Ad esempio, se stai ripetendo una gerarchia
di classi in 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 argomenti anziché un array vuoto. Questo comportamento era documentato in precedenza, ma
non era gestito correttamente in Dalvik. Le versioni precedenti di Mockito riscontrano difficoltà, pertanto utilizza una versione aggiornata di Mockito durante i test con ART.
Risolvere i problemi di compilazione AOT
La compilazione Java Ahead-Of-Time (AOT) di ART dovrebbe funzionare per tutto il codice Java standard. La compilazione viene eseguita dallo strumento dex2oat
di ART; se riscontri problemi relativi a dex2oat
al momento dell'installazione, comunicacelo (consulta la sezione Segnalazione dei problemi) per consentirci di risolverli il più rapidamente possibile. Un paio di problemi da notare:
- ART esegue una verifica con bytecode più rigorosa al momento dell'installazione rispetto a Dalvik. Il codice prodotto dagli strumenti di creazione di Android dovrebbe essere valido. Tuttavia, alcuni strumenti di post-elaborazione (in particolare quelli che eseguono l'offuscamento) potrebbero produrre file non validi tollerati da Dalvik, ma rifiutati da ART. Stiamo collaborando con i fornitori di strumenti per individuare e risolvere questi problemi. In molti casi, ottenere le versioni più recenti degli strumenti e rigenerare i file DEX può risolvere questi problemi.
- Di seguito sono riportati alcuni problemi tipici segnalati dallo strumento di verifica ART:
- flusso di controllo non valido
monitorenter
/monitorexit
sbilanciati- Dimensione elenco dei tipi di parametri di lunghezza 0
- Alcune app hanno dipendenze per il formato file
.odex
installato in/system/framework
,/data/dalvik-cache
o nella directory di output ottimizzata diDexClassLoader
. Questi file ora sono file ELF e non una forma estesa di file DEX. Anche se ART cerca di seguire le stesse regole di denominazione e blocco di Dalvik, le app non devono dipendere dal formato file; il formato è soggetto a modifiche senza preavviso.Nota: in Android 8.0 (livello API 26) e versioni successive, la directory di output ottimizzato
DexClassLoader
è stata deprecata. Per maggiori informazioni, consulta la documentazione relativa al costruttoreDexClassLoader()
.
Problemi relativi ai report
Se riscontri problemi non dovuti a JNI dell'app, segnalali tramite il tracker dei problemi del progetto open source Android all'indirizzo https://code.google.com/p/android/issue/list.
Includi un "adb bugreport"
e un link all'app nel Google Play Store, se disponibile. Altrimenti, se possibile, allega un APK che riproduca il problema. Tieni presente che i problemi (inclusi gli allegati) sono visibili pubblicamente.