Aprendiendo a usar NDK Android Day(castellano)

870 views

Published on

Aprendiendo a usar NDK Android Day presentado por Xavier Hallade en el Android Day del 06/06/2014 celebrado en Madrid

Published in: Technology
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
870
On SlideShare
0
From Embeds
0
Number of Embeds
94
Actions
Shares
0
Downloads
17
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Aprendiendo a usar NDK Android Day(castellano)

  1. 1. Usando el NDK (Native Development Kit) de Android* Xavier Hallade, Technical Marketing Engineer @ph0b - ph0b.com
  2. 2. 3 Agenda • El NDK (Native Development Kit) de Android* • Desarrollando una aplicación que utiliza el NDK • Soportando diferentes arquitecturas de CPU • Depuración (debug) y optimización • Q&A
  3. 3. 4 NDK (Native Development Kit) de Android* Qué es?  Un build de scripts/toolkit para incorporar código nativo en aplicaciones Android* a través de la Java Native Interface (JNI) Por qué utilizarlo?  Performance  e.g., algoritmos complejos, aplicaciones multimedia, juegos  Diferenciación  Aplicaciones que toman ventaja de acceder directamente a la CPU/HW  e.g., utilizando SSSE3 para optimización  Animaciones fluidas sin cortes o interrupciones  Re-utilización de código Por qué no utilizarlo?  La mejora en performance no siempre está garantizada y estamos agregando complejidad.
  4. 4. 5 Código C/C++ Makefile ndk- build Mix con Java* GDB debug Java Framework SDK APIs JNI Librerías nativas Aplicación Android* NDK APIs Librería Bionic C Desarrollo de una aplicación NDK Using JNI
  5. 5. 6 Plataforma NDK Aplicación Android* NDK Aplicación Dalvik* Archivos .class Fuente Java Compilar con Javac Librería nativa Java .class Librería nativa Java* Compilar con Javac Crear header C con javah - jni Archivo HeaderCódigo fuente C/C++ Compilar y linkear Código C Librería dinámica Archivos de la aplicación Makefile Opcional gracias a JNI_Onload
  6. 6. 7 Compatibilidad con C/C++ Standard Librería Bionic C:  Más liviana que que la librería GNU C standard  No compatible con POSIX  Incluye soporte de pthread, pero limitado  Sin System-V IPCs  Acceso a las propiedades del sistema Android* Bionic no es compatible en relación al binario con la librería C standard Esto significa que generalmente deberás (re)compilar todo utilizando la toolchain del NDK Android.
  7. 7. 8 Soporte Android* para C++ Por defecto se utiliza el sistema. Le falta:  Soporte de librería standard C++ (excepto algunos headers)  Soporte a excepciones de C++  Soporte RTTI Afortunadamente, tienes otras librerías disponibles con el NDK: Runtime Exceptions RTTI STL system No No No gabi++ Sí Sí No stlport Sí Sí Sí gnustl Sí Sí Sí libc++ Sí Sí Sí Elige contra qué librería quieres compilar en tu Makefile (Application.mk file): APP_STL := gnustl_shared Postfix el runtime con _static o _shared Para utilizar capacidades C++, también debes habilitarlas en tu Makefile: LOCAL_CPP_FEATURES += exceptions rtti
  8. 8. 9 PSI TS PIDs Instalando el NDK Android* NDK es un archive dependiente de la plataforma: Provee:  Un ambiente de build  Headers y librerías Android*  Documentación y muestras de código (muy útiles) Puedes integrar con Eclipse ADT:
  9. 9. 10 Estructura Standard de un proyecto Android* Fuentes nativas – carpeta JNI 1. Creación de la carpeta JNI para las Fuentes nativas 3. Creación del Makefile Android.mk 4. Construir librerías nativas usando el script NDK-BUILD 2. Reutiliza o crea Fuentes nativas c/c++ NDK-BUILD creará automáticamente las carpetas de librerías ABI. Agregar manualmente código nativo a un proyecto Android*
  10. 10. 11 Agregar soporte NDK a tu proyecto Android* en Eclipse
  11. 11. 12 Android* NDK Samples App de muestra Tipo hello-jni Llama a una función nativa escrita en C desde Java*. bitmap-plasma Accede a un objeto Android* Bitmap desde C. san-angeles Códiog EGL y OpenGL* ES en C. hello-gl2 Setup EGL en Java y código OpenGL ES en C. native-activity Muestra OpenGL solo en C (sin Java, usa la clase NativeActivity). native-plasma Muestra OpenGL solo en C (también usa la clase NativeActivity). …
  12. 12. 13 El foco en Actividades Nativas Sólo código nativo en el proyecto android_main(): es el punto de entrada en su propio hilo de ejecución Event loop to get input data and frame drawing messages /** * This is the main entry point of a native application that is using * android_native_app_glue. It runs in its own thread, with its own * event loop for receiving input events and doing other things. */ void android_main(struct android_app* state); “Éste es el punto de entrada principal a una app nativa que utiliza android_native_app_glue,. corre en su propio hilo de ejecucion, con su propio bucle de eventos para recibir eventos de entrada y ejecutar otras tareas.”
  13. 13. 14 PSI TS PIDs Integrando funciones nativas con Java* Declara métodos nativos en tu app Android* (Java*) usando la palabra clave “nativa": public native String stringFromJNI(); Proporciona una librería native compartida construida con el NDK que contiene los métodos usados por tu aplicación: libMyLib.so Tu aplicación deber cargar la librería compartida (antes de usarla… durante la carga de la clase, por ejemplo): static { System.loadLibrary("MyLib"); } Hay dos formas de asociar tu código nativo a los métodos Java: javah y JNI_OnLoad
  14. 14. 15 Método Javah “javah” ayuda a generar automáticamente los headers JNI apropiados basados en los archivos fuente Java de los archivos compilados de los archivos de clase Java. Ejemplo: > javah –d jni –classpath bin/classes com.example.hellojni.HelloJni Genera el archivo com_example_hellojni_HelloJni.h con esta definción: JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv *, jobject);
  15. 15. 16 Javah Method La función C que será mapeada automáticamente: jstring Java_com_example_hellojni_HelloJni_stringFromJNI(JNIEnv* env, jobject thiz ) { return (*env)->NewStringUTF(env, "Hello from JNI !"); } ... { ... tv.setText( stringFromJNI() ); ... } public native String stringFromJNI(); static { System.loadLibrary("hello-jni"); }
  16. 16. 17 Método JNI_OnLoad – Por qué? Método probado No más sorpresas luego de registrar métodos Menos propenso a errores al refactorizar Agregar/eliminar funciones nativas fácilmente Sin problemas de tabla de símbolos al mezclar código C/C++ La mejor forma para cachear referencias a clases y objetos Java*
  17. 17. 18 Método JNI_OnLoad En tu librería nombra las funciones a tu gusto y declara el mapeo con métodos JVM: jstring stringFromJNI(JNIEnv* env, jobject thiz) { return env->NewStringUTF("Hello from JNI !");} static JNINativeMethod exposedMethods[] = { {"stringFromJNI","()Ljava/lang/String;",(void*)stringFromJNI}, } ()Ljava/lang/String; es la firma JNI del método Java*, que puedes recuperar usando la utilidad javap: > javap -s -classpath binclasses -p com.example.hellojni.HelloJni Compiled from "HelloJni.java“ … public native java.lang.String stringFromJNI(); Signature: ()Ljava/lang/String; …
  18. 18. 19 Método JNI_OnLoad JNI_OnLoad es el punto de entrada de la librería llamado durante la carga. Aquí aplica el mapeo definido previamente. extern "C" jint JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR; jclass clazz = env->FindClass("com/example/hellojni/HelloJni"); if(clazz==NULL) return JNI_ERR; env->RegisterNatives(clazz, exposedMethods, sizeof(exposedMethods)/sizeof(JNINativeMethod)); env->DeleteLocalRef(clazz); return JNI_VERSION_1_6; }
  19. 19. 20 Manejo de memoria en objetos Java* El manejo de memoria de objetos Java* es realizado por la JVM: • Tú solo te encargas de las referencias a esos objetos • Cada vez que obtienes una referencia, debes recordar eliminarla luego de su uso • Las referencias locales son eliminadas automáticamente cuando la llamada nativa vuelve a Java • Las referencias son locales por defecto • Las referencias globales solo son creadas por NewGlobalRef()
  20. 20. 21 Creando una cadena Java* La memoria es manejada por la JVM, jstring siempre es una referencia. Puedes hacer un llamado a DeleteLocalRef() una vez que has terminado con ella. La diferencia principal de compilar código JNI en C o en C++ es la naturaleza de “env” como se ve aquí. Recuerda que más allá de eso, la API es la misma. C: jstring string = (*env)->NewStringUTF(env, "new Java String"); C++: jstring string = env->NewStringUTF("new Java String");
  21. 21. 22 Obteniendo una cadena C/C++ desde una cadena Java* const char *nativeString = (*env)- >GetStringUTFChars(javaString, null); … (*env)->ReleaseStringUTFChars(env, javaString, nativeString); //más seguro int tmpjstrlen = env->GetStringUTFLength(tmpjstr); char* fname = new char[tmpjstrlen + 1]; env->GetStringUTFRegion(tmpjstr, 0, tmpjstrlen, fname); fname[tmpjstrlen] = 0; … delete fname;
  22. 22. 23 Manejando excepciones Java* // llamar a métodos java puede arrojar excepciones Java jthrowable ex = (*env)->ExceptionOccurred(env); if (ex!=NULL) { (*env)->ExceptionClear(env); // manejar la excepción } (*env)->DeleteLocalRef(env, ex);
  23. 23. 24 Tipos primitivos JNI Tipos Java* Tipos Nativos Descripción boolean jboolean unsigned 8 bits byte jbyte signed 8 bits char jchar unsigned 16 bits short jshort signed 16 bits int jint signed 32 bits long jlong signed 64 bits float jfloat 32 bits double jdouble 64 bits void void N/A
  24. 24. 25 Tipos de Referencias JNI jobject jclass jstring jarray jobjectArray jbooleanArray jbyteArray jcharArray jshortArray jintArray jlongArray jfloatArray jdoubleArray jthrowable Los elementos Arrays se manipulan usando Get<type>ArrayElements() and Get/Set<type>ArrayRegion() No olvides llamar ReleaseXXX() para cada llamado GetXXX()
  25. 25. 26 Llamando métodos Java* En una instancia de objeto: jclass clazz = (*env)->GetObjectClass(env, obj); jmethodID mid = (*env)->GetMethodID(env, clazz, "methodName", "(…)…"); if (mid != NULL) (*env)->Call<Type>Method(env, obj, mid, parameters…); Llamado estático: jclass clazz = (*env)->FindClass(env, "java/lang/String"); jmethodID mid = (*env)->GetStaticMethodID(env, clazz, "methodName", "(…)…"); if (mid != NULL) (*env)->CallStatic<Type>Method(env, clazz, mid, parameters…); • (…)…: method signature • Parámetros: lista de parámetros esperados por el método Java* • <Type>: Java method return type
  26. 26. 27 Lanzando excepciones Java* jclass clazz = (*env->FindClass(env, "java/lang/Exception"); if (clazz!=NULL) (*env)->ThrowNew(env, clazz, "Message"); La excepción será lanzada solo cuando la llamada JNI vuelve a Java*, no romperá la ejecución en curso de código nativo
  27. 27. 28 Incluye todos los ABIs seteando APP_ABI a all en jni/Application.mk: APP_ABI=all El NDK generará código optimizado para todos los ABIs objetivo También puedes pasar la variable APP_ABI a ndk-build, y especificar cada ABI: ndk-build APP_ABI=x86 NDK: configurando los ABIs objetivos Build ARM v7a libs Build ARM v5 libs Build x86 libs Build mips libs
  28. 28. 33 Debugging con logcat El NDK provee una API de API en <android/log.h>: Normalmente utilizado a través de este tipo de macro: #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "APPTAG", __VA_ARGS__)) Ejemplo de uso: LOGI("accelerometer: x=%f y=%f z=%f", x, y, z); int __android_log_print(int prio, const char *tag, const char *fmt, ...)
  29. 29. 34 Debugging con logcat Para obtener más información en ejecución de código nativo: adb shell setprop debug.checkjni 1 (habilitado por defecto en el emulador) Y para obtener más información sobre depuración de memoria (solo root): adb shell setprop libc.debug.malloc 1 -> detección de pérdidas adb shell setprop libc.debug.malloc 10 -> Detección de overrun adb shell start/stop -> ambiente de reload
  30. 30. 35 Debugging con GDB y Eclipse Soporte nativo debe ser agregado a tu proyecto Pasa NDK_DEBUG=1 APP_OPTIM=debug al commando ndk-build, desde las propiedades del proyecto: La bandera (flag) NDK_DEBUG debería ser automáticamente seteada para un build de debug pero no ocurre en este caso.
  31. 31. 36 Debugging con GDB y Eclipse* Cuando NDK_DEBUG=1 es especificado, un archivo “gdbserver” se agrega a tus librerías
  32. 32. 37 Debugging con GDB y Eclipse* Depura tu proyecto como una aplicación nativa Android*:
  33. 33. 38 Debugging con GDB y Eclipse Desde la perspectiva de depuración de Eclipse tu puedes manipular breakpoints y depurar tu proyecto Tu aplicación correrá antes de que se adjunte el debugger, los breakpoints que establezcas cerca del lanzamiento de la app serán ignorados.
  34. 34. 40 Flags GCC ffast-math influence round-off of fp arithmetic and so breaks strict IEEE compliance Las otras optimizaciones son completamente seguras Agrega -ftree-vectorizer-verbose para obtener un reporte de vectorización ifeq ($(TARGET_ARCH_ABI),x86) LOCAL_CFLAGS += -ffast-math -mtune=atom -msse3 -mfpmath=sse else LOCAL_CFLAGS += ... endif LOCAL_CFLAGS += -O3 -ffast-math -mtune=slm -msse4.2 -mfpmath=sse Para optimizar para la microarquitectura Intel Silvermont (disponible a partir de NDK r9 gcc-4.8 toolchain): Optimization Notice Intel's compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSSE3 instruction sets and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any optimization on microprocessors not manufactured by Intel. Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the applicable product User and Reference Guides for more information regarding the specific instruction sets covered by this notice. Notice revision #20110804
  35. 35. 41 Vectorización Las instrucciones SIMD hasta SSSE3 está disponible en arquitecturas basadas en el procesador Intel® Atom™, Intel® SSE4.2 en la microarquitectura Intel Silvermont En ARM*, puedes obtener vectorización a través de las instrucciones de ARM NEON* Dos formas clásicas de utilizer estas instrucciones: • Compilador de auto-vectorization • Intrínseco del compilador Optimization Notice Intel's compilers may or may not optimize to the same degree for non-Intel microprocessors for optimizations that are not unique to Intel microprocessors. These optimizations include SSE2, SSE3, and SSSE3 instruction sets and other optimizations. Intel does not guarantee the availability, functionality, or effectiveness of any optimization on microprocessors not manufactured by Intel. Microprocessor-dependent optimizations in this product are intended for use with Intel microprocessors. Certain optimizations not specific to Intel microarchitecture are reserved for Intel microprocessors. Please refer to the applicable product User and Reference Guides for more information regarding the specific instruction sets covered by this notice. Notice revision #20110804 SSSE3, SSE4.2 Vector size: 128 bit Data types: • 8, 16, 32, 64 bit integer • 32 and 64 bit float VL: 2, 4, 8, 16 X2 Y2 X2◦Y2 X1 Y1 X1◦Y1 X4 Y4 X4◦Y4 X3 Y3 X3◦Y3 127 0
  36. 36. Q&A xavier.hallade@intel.com @ph0b – ph0b.com

×