O Android Runtime (ART) é o tempo de execução padrão para dispositivos que executam o Android 5.0 (API de nível 21) e versões mais recentes. Esse ambiente de execução oferece vários recursos que melhoram o desempenho e a suavidade da plataforma e dos apps Android. Saiba mais sobre os novos recursos do ART em Introdução ao ART.
No entanto, algumas técnicas que funcionam na Dalvik não funcionam no ART. Este documento traz informações a serem observadas ao migrar um app existente para que ele seja compatível com o ART. A maioria dos apps precisa funcionar com o ART.
Solução de problemas de coleta de lixo (GC)
Na Dalvik, os apps acham útil chamar explicitamente
System.gc()
para solicitar a coleta de lixo (GC). Isso será
menos necessário com o ART, especialmente se você estiver invocando a coleta de lixo
para evitar ocorrências do tipo GC_FOR_ALLOC
ou reduzir a fragmentação. Verifique qual ambiente de execução está em uso
chamando System.getProperty("java.vm.version")
. Se o ART estiver em uso, o valor da propriedade
será "2.0.0"
ou maior.
O ART usa o coletor de cópia simultânea (CC, na sigla em inglês), que compacta simultaneamente a heap Java. Por isso, evite o uso de técnicas incompatíveis com a GC com compactação (como salvar ponteiros em dados de instância do objeto). Isso é particularmente importante para apps que usam a Java Native Interface (JNI). Para mais informações, consulte Como evitar problemas de JNI.
Prevenção de problemas de JNI
O JNI do ART é um pouco mais rigoroso que o da Dalvik. É altamente recomendável usar o modo CheckJNI para detectar problemas comuns. Se o app usa código C/C++, consulte este artigo:
Como depurar JNI do Android com o CheckJNI
Verificação do código JNI para problemas de coleta de lixo
O coletor de cópias simultâneas (CC) pode mover objetos na memória para compactação. Se você usar o código C/C++, não execute operações incompatíveis com a GC com compactação. Melhoria do CheckJNI para identificar possíveis problemas, conforme descrito em Mudanças na referência local da JNI no ICS (link em inglês).
Uma área a ser observada em particular é o uso das
funções
Get...ArrayElements()
e Release...ArrayElements()
. Em ambientes de execução com GC sem compactação, as
funções Get...ArrayElements()
normalmente retornam uma referência à
memória real compatível com o objeto de matriz. Se você fizer uma alteração em um dos
elementos de matriz retornados, o objeto de matriz em si será alterado (e os argumentos
de Release...ArrayElements()
geralmente serão ignorados). No entanto, se
a GC com compactação estiver em uso, as funções Get...ArrayElements()
poderão
retornar uma cópia da memória. Se você usar a referência de forma incorreta enquanto a GC com compactação estiver
em uso, isso pode corromper a memória ou causar outros problemas. Por exemplo:
- Se você fizer alguma alteração nos elementos da matriz retornados, chame a função
Release...ArrayElements()
apropriada quando terminar para garantir que as alterações feitas sejam copiadas corretamente de volta para o objeto de matriz subjacente. - Quando liberar os elementos de matriz de memória, você precisa usar o modo
apropriado, dependendo das mudanças feitas:
- Se você não fez nenhuma mudança nos elementos da matriz, use o modo
JNI_ABORT
, que libera a memória sem copiar as mudanças no objeto de matriz subjacente. - Se você tiver feito alterações na matriz e não precisar mais da referência, use o código
0
, que atualiza o objeto de matriz e libera a cópia da memória. - Se você fez alterações na matriz que quer confirmar e quer manter a cópia da matriz, use
JNI_COMMIT
, que atualiza o objeto de matriz subjacente e mantém a cópia.
- Se você não fez nenhuma mudança nos elementos da matriz, use o modo
- Ao chamar
Release...ArrayElements()
, retorne o mesmo ponteiro que foi originalmente retornado porGet...ArrayElements()
. Por exemplo, não é seguro incrementar o ponteiro original para verificar os elementos da matriz retornados e, em seguida, transmitir o ponteiro incrementado paraRelease...ArrayElements()
. Passar esse ponteiro modificado pode fazer com que a memória incorreta seja liberada, resultando no corrupção da memória.
Tratamento de erros
A JNI do ART gera erros em alguns casos em que o Dalvik não ocorre. Mais uma vez, você pode identificar muitos desses casos testando com o CheckJNI.
Por exemplo, se RegisterNatives
for chamado com um método que
não existe (talvez porque ele foi removido por uma ferramenta como o
ProGuard), o ART agora gera NoSuchMethodError
corretamente:
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)
O ART também registra um erro (visível no logcat) se RegisterNatives
for
chamado sem métodos:
W/art ( 1234): JNI RegisterNativeMethods: attempt to register 0 native methods for <classname>
Além disso, as funções JNI GetFieldID()
e
GetStaticFieldID()
agora geram NoSuchFieldError
corretamente em vez de simplesmente retornar um valor nulo Da mesma forma, GetMethodID()
e
GetStaticMethodID()
agora geram NoSuchMethodError
corretamente
Isso pode levar a falhas de CheckJNI devido às exceções não processadas ou às
exceções geradas para autores de chamada do Java do código nativo. Isso torna
especialmente importante o teste de apps compatíveis com o ART com o modo CheckJNI.
O ART espera que os usuários dos métodos CallNonvirtual...Method()
da JNI
(como CallNonvirtualVoidMethod()
) usem a classe de declaração
do método, não uma subclasse, conforme exigido pela especificação da JNI.
Prevenção de problemas de tamanho da pilha
A Dalvik tinha pilhas separadas para códigos nativo e Java, com um tamanho de pilha Java
padrão de 32 KB e um tamanho de pilha nativa padrão de 1 MB. O ART tem uma pilha unificada
para oferecer uma localidade melhor. Normalmente, o tamanho da pilha Thread
do ART
precisa ser aproximadamente o mesmo da Dalvik. No entanto, se você definir
tamanhos de pilha explicitamente, pode ser necessário revisar esses valores para apps em execução no
ART.
- Em Java, revise as chamadas para o construtor
Thread
que especificam um tamanho de pilha explícito. Por exemplo, será necessário aumentar o tamanho seStackOverflowError
ocorrer. - Em C/C++, revise o uso de
pthread_attr_setstack()
epthread_attr_setstacksize()
para linhas de execução que também executam o código Java via JNI. Veja um exemplo do erro registrado quando um app tenta chamarAttachCurrentThread()
da JNI quando o tamanho da linha de execução é muito pequeno:F/art: art/runtime/thread.cc:435] Attempt to attach a thread with a too-small stack (16384 bytes)
Mudanças no modelo de objeto
A Dalvik permitiu incorretamente que a subclasse modificasse os métodos privados do pacote. O ART enviará um aviso nestes casos:
Before Android 4.1, method void com.foo.Bar.quux() would have incorrectly overridden the package-private method in com.quux.Quux
Se você pretende modificar o método de uma classe em um pacote diferente, declare o
método como public
ou protected
.
Object
agora tem campos particulares Apps que refletem sobre os campos
das hierarquias de classes precisam ter cuidado para não tentar examinar os
campos de Object
. Por exemplo, se você estiver iterando uma hierarquia
de classes como parte de um framework de serialização, pare quando
Class.getSuperclass() == java.lang.Object.class
em vez de continuar até que o método retorne null
.
O proxy InvocationHandler.invoke()
agora recebe null
se não houver
argumentos em vez de uma matriz vazia. Esse comportamento foi documentado anteriormente, mas
não foi processado corretamente na Dalvik. Versões anteriores do Mockito têm dificuldades com
isso. Portanto, use uma versão atualizada do Mockito ao testar com o ART.
Correção de problemas de compilação de AOT
A compilação Java antecipada (AOT, na sigla em inglês) do ART precisa funcionar para todos os códigos Java
padrão. A compilação é realizada pela ferramenta dex2oat
do ART. Se você encontrar algum problema relacionado ao
dex2oat
no momento da instalação, entre em contato conosco (consulte Como informar problemas) para que possamos corrigi-los o mais
rápido possível. Algumas observações necessárias:
- O ART realiza uma verificação de bytecode mais rígida no momento da instalação do que a Dalvik. O código produzido por ferramentas de compilação Android deve funcionar normalmente. No entanto, algumas ferramentas de pós-processamento (especialmente ferramentas que executam ofuscação) podem produzir arquivos inválidos que são tolerados pela Dalvik, mas rejeitados pelo ART. Estamos trabalhando com fornecedores de ferramentas para encontrar e corrigir esses problemas. Em muitos casos, instalar as versões mais recentes das ferramentas e gerar novamente os arquivos DEX pode corrigir esses problemas.
- Estes são alguns problemas comuns sinalizados pelo verificador do ART:
- controle de fluxo inválido
- desequilibrado
monitorenter
/monitorexit
- tamanho de lista de tipo de parâmetro 0
- Alguns apps têm dependências no formato de arquivo
.odex
instalado em/system/framework
,/data/dalvik-cache
ou no diretório de saída otimizado deDexClassLoader
. Esses arquivos agora são arquivos ELF e não uma forma estendida de arquivos DEX. Embora o ART tente seguir as mesmas regras de nomenclatura e bloqueio que a Dalvik, os apps não podem depender do formato de arquivo, porque ele está sujeito a mudanças sem aviso prévio.Observação:no Android 8.0 (API de nível 26) e versões mais recentes, o diretório de saída otimizado
DexClassLoader
foi descontinuado. Para mais informações, consulte a documentação do construtorDexClassLoader()
.
Informar problemas
Se você encontrar algum problema que não seja causado pela JNI do app, informe-o pelo Android Open Source Project Issue Tracker em https://code.google.com/p/android/issues/list.
Inclua um "adb bugreport"
e um link para o app na Google
Play Store, se disponível. Caso contrário, se possível, anexe um APK que reproduza
o problema. Os problemas (incluindo anexos) são visíveis
publicamente.