Verificar el comportamiento de la aplicación en el tiempo de ejecución de Android (ART)

Android Runtime (ART) es el entorno de ejecución predeterminado para dispositivos que ejecutan Android 5.0 (nivel de API 21) y versiones posteriores Este entorno de ejecución ofrece varias funciones que mejoran el rendimiento y la fluidez de la plataforma y las apps de Android. Encuentra más información sobre las nuevas funciones de ART en Introducción ARTE

Sin embargo, algunas técnicas que funcionan en Dalvik no sirven en ART. Esta te permite saber qué aspectos hay que tener en cuenta al migrar un archivo para que sea compatible con ART. La mayoría de las apps solo deberían funcionar cuando se ejecutan con ART.

Cómo afrontar problemas de recolección de elementos no utilizados (GC)

Con Dalvik, en las apps, a menudo, resulta útil llamar de manera explícita System.gc() para solicitar la recolección de elementos no utilizados (GC) Debe ser mucho menos necesaria con ART, en especial si invocas la recolección de elementos no utilizados para evitar el tipo GC_FOR_ALLOC o reducir la fragmentación. Puedes verificar qué entorno de ejecución está en uso llamando a System.getProperty("java.vm.version"). Si ART está en uso, el valor de la propiedad es "2.0.0" o superior.

ART usa un recopilador de copia simultánea (CC), que compacta el montón de Java al mismo tiempo. Debido a esto, debes evitar el uso de técnicas incompatibles con la compactación de recolección de elementos no utilizados (como guardar punteros en el objeto datos de la instancia). Esto es particularmente importante para las aplicaciones que usan el Interfaz nativa de Java (JNI). Para obtener más información, consulta Cómo evitar problemas de la JNI.

Prevención de problemas de la JNI

La JNI del ART es un poco más estricta que la de Dalvik. Es especialmente una buena idea usar el modo CheckJNI para detectar problemas comunes. Si tu app usa C/C++ código, debes consultar el siguiente artículo:

Depuración JNI de Android con CheckJNI

Verificación del código de la JNI en busca de problemas de recolección de elementos no utilizados

El colector de Copia simultánea (CC) puede mover objetos en la memoria para la compactación. Si usas código C/C++, no realizar operaciones que son incompatibles con el recolector con compactación de elementos no utilizados. Mejoramos CheckJNI para identificar algunos problemas potenciales (como se describe en JNI Cambios de referencias locales en ICS).

Un área que debe observarse en particular es el uso de Get...ArrayElements() y Release...ArrayElements() funciones. En entornos de ejecución con recolección sin compactar, el Las funciones Get...ArrayElements() suelen mostrar una referencia al la memoria real que respalda el objeto del array. Si realizas un cambio en uno de los los elementos de array que se devuelven, el objeto de array se cambia a sí mismo (y los argumentos a Release...ArrayElements() se suelen ignorar). Sin embargo, si se está usando la recolección con compactación de elementos no utilizados, las funciones Get...ArrayElements() pueden devuelve una copia del recuerdo. Si haces un uso inadecuado de la referencia cuando se compacta el recolector con compactación de elementos no utilizados, en uso, puede dañar la memoria u otros problemas. Por ejemplo:

  • Si realizas algún cambio en los elementos del array que se devuelven, debes llamar al la función Release...ArrayElements() adecuada cuando hayas terminado, para asegurarte de que los cambios que hiciste se copien correctamente en la base de datos Array.
  • Cuando lanzas los elementos del array de memoria, debes usar los según los cambios que hayas realizado:
    • Si no realizaste ningún cambio en los elementos del array, usa Modo JNI_ABORT, que libera la memoria sin copiar cambia de vuelta al objeto de array subyacente.
    • Si realizaste cambios en el array y no necesitas la referencia más, usa el código 0 (que actualiza el objeto del array y libera la copia del recuerdo).
    • Si modificaste el array que quieres confirmar y quieres Para mantener la copia del array, usa JNI_COMMIT (que actualiza el objeto de array subyacente y retiene la copia).
  • Cuando llames a Release...ArrayElements(), se mostrará el mismo que mostró originalmente Get...ArrayElements(). Para ejemplo, no es seguro aumentar el puntero original (para buscar los elementos de array que se muestran) y, luego, pasa el puntero incrementado a Release...ArrayElements() Pasar este puntero modificado puede causar la memoria incorrecta para liberarse, lo que genera daños en la memoria.

Manejo de errores

La JNI del ART genera errores en varios casos en los que Dalvik no lo hace. (Una vez De nuevo, puedes detectar muchos casos realizando pruebas con CheckJNI).

Por ejemplo, si se llama a RegisterNatives con un método que no existe (quizás porque el método fue eliminado por una herramienta como ProGuard), ART ahora arroja NoSuchMethodError correctamente:

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 también registra un error (visible en logcat) si RegisterNatives es llamada sin métodos:

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

Además, las funciones JNI GetFieldID() y GetStaticFieldID() ahora arroja NoSuchFieldError correctamente. en lugar de mostrar un valor nulo. De manera similar, GetMethodID() y GetStaticMethodID() ahora arroja NoSuchMethodError correctamente Esto puede provocar fallas en CheckJNI debido a las excepciones no controladas o la excepciones que se arrojan a los emisores de Java de código nativo. Esto hace que es particularmente importante probar apps compatibles con el ART a través del modo CheckJNI.

ART espera usuarios de los métodos CallNonvirtual...Method() de JNI (como CallNonvirtualVoidMethod()) para usar el método no una subclase, como lo exige la especificación de JNI.

Prevención de problemas de tamaño de pila

Dalvik tenía pilas separadas para el código nativo y el código Java con un código Java predeterminado tamaño de pila de 32 KB y un tamaño de pila nativa predeterminado de 1 MB. ART tiene una aplicación para una mejor localidad. Por lo general, la pila Thread de ART el tamaño debería ser aproximadamente el mismo que para Dalvik. Sin embargo, si explícitamente y configurar tamaños de pila, quizás debas revisar esos valores para las apps que se ejecutan en ART.

  • En Java, revisa las llamadas al constructor Thread que especifican una pila explícita de tamaño del ensamble. Por ejemplo, deberás aumentar el tamaño si se produce StackOverflowError.
  • En C/C++, revisa el uso de pthread_attr_setstack() y pthread_attr_setstacksize() para subprocesos que también ejecutan código Java mediante JNI A continuación, se muestra un ejemplo del error que se registra cuando una app intenta llamar a JNI AttachCurrentThread() cuando el tamaño de pthread es demasiado pequeño:
    F/art: art/runtime/thread.cc:435]
        Attempt to attach a thread with a too-small stack (16384 bytes)

Cambios de modelo de objeto

Dalvik permitió de modo incorrecto que las subclases anulen los métodos privados de paquetes. El ART emite una precaución en dichos casos:

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

Si quieres anular un método de una clase en un paquete diferente, declara la método como public o protected.

Object ahora tiene campos privados. Apps sobre campos en las jerarquías de sus clases, deben tener cuidado de no observar las campos de Object. Por ejemplo, si realizas una iteración en una clase de Terraform como parte de un framework de serialización,

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

en lugar de continuar hasta que el método muestre null.

El proxy InvocationHandler.invoke() ahora recibe null si no hay en lugar de un array vacío. Este comportamiento se documentó anteriormente, pero no se maneja de forma correcta en Dalvik. Las versiones anteriores de Mockito tienen dificultades con lo siguiente: así que usa una versión actualizada de Mockito cuando realices pruebas con el ART.

Solución de problemas de compilación de AOT

La compilación de Java por adelantado (AOT) de ART debería funcionar con todas las versiones estándar de Java. código. La compilación es interpretada por ART's Herramienta dex2oat; si tienes algún problema relacionado con dex2oat en el momento de la instalación, avísanos (consulta Cómo informar problemas) para que podamos solucionarlos lo antes posible de la forma más eficaz posible. Algunos problemas que deben tenerse en cuenta:

  • Durante la instalación, el ART realiza una verificación de código de bytes más estricta que la de Dalvik. El código que producen las herramientas de compilación de Android debe funcionar bien. Sin embargo, algunos las herramientas de procesamiento posterior (especialmente las que realizan ofuscación) pueden producir archivos no válidos que Dalvik tolera y ART rechaza. Hemos estado trabajar con proveedores de herramientas para encontrar y solucionar esos problemas. En muchos casos, obtener las versiones más recientes de tus herramientas y la regeneración de los archivos DEX puede solucionar estos problemas problemas.
  • Estos son algunos de los problemas típicos que marca el verificador del ART:
    • flujo de control no válido
    • desequilibrado monitorenter/monitorexit
    • tamaño de lista del tipo de parámetro de longitud 0
  • Algunas apps tienen dependencias en el archivo .odex instalado en /system/framework, /data/dalvik-cache o en el directorio de salida optimizado de DexClassLoader. Estos ahora son archivos ELF y no formas extendidas de archivos DEX. Mientras ART intenta las mismas reglas de nomenclatura y bloqueo de Dalvik, las apps no deberían en el formato de archivo; el formato está sujeto a cambios sin previo aviso.

    Nota: En Android 8.0 (nivel de API 26) y superior, el directorio de salida optimizado DexClassLoader quedó obsoleto. Para obtener más información, consulta la documentación del DexClassLoader() .

Cómo informar problemas

Si tienes algún problema que no se deba a problemas de JNI de la app, infórmalo a través de la Herramienta de seguimiento de errores del Proyecto de código abierto de Android en https://code.google.com/p/android/issues/list. Incluye un "adb bugreport" y un vínculo a la app en la app de Google Play Store (si está disponible). De lo contrario, si es posible, adjunta un APK que se reproduzca el problema. Ten en cuenta que los problemas (incluidos los archivos adjuntos) se sean visibles.