Дмитрий Юницкий. «Android NDK или как я перестал бояться и...

22
Android NDK, или как я перестал бояться и полюбил нативную разработку.

Transcript of Дмитрий Юницкий. «Android NDK или как я перестал бояться и...

Page 1: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Android NDK, или как я перестал бояться и полюбил

нативную разработку.

Page 2: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Android NDK.• JNI - набор инструментов для запуска кода на C/

C++/Ассемблере из виртуальной машины Java

• Вспомогательная нативная библиотека(скомпилированная для каждой поддерживаемой ABI) или полностью нативное приложение используя NativeActivity

• Java -> нативный код, нативный код -> Java, Java -> нативный код -> Java…

Page 3: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Зачем?

• Критичные по производительности участки кода

• Использование написанного ранее кода или существующих библиотек (OpenCV, ffmpeg, …)

• Написание кроссплатформенного кода для нескольких платформ(бизнес-логика, алгоритмы и тд)

Page 4: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Какие возникают сложности?

• Работа с jni из нативных потоков

• Отладка

• Возрастающий размер результирующей apk

• Локальные/глобальные ссылки

• Дорогой переход из java кода в нативный (и обратно)

• Прочие приятные сюрпризы от jni)

Page 5: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Базовый пример.public class IntegrationActivity extends AppCompatActivity{ … @Override protected void onCreate(Bundle savedInstanceState) { … textView.setText("Value from native code: " + nativeGetBooleanValue()); } public native boolean nativeGetBooleanValue(); static { System.loadLibrary("ndk_integration"); }}

#include <jni.h>#include "SomeCppClass.hpp"extern "C"{ JNIEXPORT jboolean JNICALLJava_my_test_integration_IntegrationActivity_nativeGetBooleanValue(JNIEnv *env, jobject instance) { SomeCppClass object; return (jboolean) object.getSomeValue();} } // extern "C"

Java

C++

Page 6: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Типы данных JNI.#ifdef HAVE_INTTYPES_H# include <inttypes.h> /* C99 */typedef uint8_t jboolean; /* unsigned 8 bits */ typedef int8_t jbyte; /* signed 8 bits */typedef uint16_t jchar; /* unsigned 16 bits */ typedef int16_t jshort; /* signed 16 bits */typedef int32_t jint; /* signed 32 bits */typedef int64_t jlong; /* signed 64 bits */typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ #elsetypedef unsigned char jboolean; /* unsigned 8 bits */ typedef signed char jbyte; /* signed 8 bits */typedef unsigned short jchar; /* unsigned 16 bits */ typedef short jshort; /* signed 16 bits */typedef int jint; /* signed 32 bits */typedef long long jlong; /* signed 64 bits */typedef float jfloat; /* 32-bit IEEE 754 */ typedef double jdouble; /* 64-bit IEEE 754 */ #endif

typedef void* jobject;typedef jobject jclass;typedef jobject jstring;typedef jobject jarray;typedef jarray jobjectArray;typedef jarray jbooleanArray;typedef jarray jbyteArray;typedef jarray jcharArray;typedef jarray jshortArray;typedef jarray jintArray;typedef jarray jlongArray;typedef jarray jfloatArray;typedef jarray jdoubleArray;typedef jobject jthrowable;typedef jobject jweak;

Примитивные Ссылочный

Page 7: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

JNIEnv и функции JNI.

• JNIEnv

• IsSameObject, Call*Method, Get*Field, Set*Field, New*Array…

• NewGlobalRef, DeleteGlobalRef, DeleteLocalRef

• AttachCurrentThread, DetachCurrentThread

• JNI_OnLoad, JNI_OnUnload

Page 8: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Локальные и глобальные ссылки.

• Объекты, на которые есть ссылки - не могут быть очищены GC

• Локальные ссылки - в пределах жизни метода в рамках одного потока

• Java VM автоматически очищает ссылки при возврате из нативного метода

• Есть предел на количество создаваемых локальных ссылок

• Глобальные ссылки действительны вплоть до явного освобождения

Page 9: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Таблица локальных и глобальных ссылок.

void DumpDalvikReferenceTables(){ JNIEnv * env = jni::GetEnv(); jclass vm_class = env->FindClass("dalvik/system/VMDebug"); jmethodID dump_mid = env->GetStaticMethodID(vm_class, "dumpReferenceTables", "()V"); env->CallStaticVoidMethod(vm_class, dump_mid); env->DeleteLocalRef(vm_class);}

Page 10: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Нативные потоки.• AttachCurrentThread для работы с jni

• DetachCurrentThread перед завершением

• Локальные ссылки НЕ очищаются - нужно всегда(!) очищать все созданные ссылки с помощью DeleteLocalRef либо создавать отдельный пул ссылок при входе в метод через PushLocalFrame/PopLocalFrame

• FindClass НЕ работает

Page 11: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Нативные потоки. FindClass.

• Простой вариант решения - кешировать класс в JNI_OnLoad.

• Более сложный, но гибкий - кешировать сам ClassLoader.

Page 12: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Кеширование ClassLoader.

JNIEXPORT jint JNICALLJNI_OnLoad(JavaVM *jvm, void *) { g_jvm = jvm; JNIEnv *env = jni::GetEnv(); auto randomClass = env->FindClass("my/test/integration/IntegrationActivity"); auto classClass = env->GetObjectClass(randomClass); auto classLoaderClass = env->FindClass("java/lang/ClassLoader"); auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader", "()Ljava/lang/ClassLoader;"); g_classLoader = env->NewGlobalRef(env->CallObjectMethod(randomClass, getClassLoaderMethod)); g_findClassMethod = env->GetMethodID(classLoaderClass, "findClass", "(Ljava/lang/String;)Ljava/lang/Class;"); return JNI_VERSION_1_6;

Page 13: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Кеширование jClass, jMethodId, jFieldId.

• jClass -локальные ссылки, для кеширования необходимо преобразовывать в глобальные.

• jMethodId, jFieldId - просто структуры, можно записывать без преобразования в статические или глобальные переменные и использовать в любых потоках.

Page 14: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Кеширование jClass, jMethodId, jFieldId.

• jClass -локальные ссылки, для кеширования необходимо преобразовывать в глобальные.

• jMethodId, jFieldId - просто структуры, можно записывать без преобразования в статические или глобальные переменные и использовать в любых потоках.

• В некоторых случаях работа через jni может быть ЗНАЧИТЕЛЬНО медленнее аналогичного кода на Java.

Page 15: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Запуск native метода из Java.

• Создать новый стекфрейм

• Передать аргументы согласно ABI

• Передать JNIEnv* и jclass(jobject)

• Synchronized

• Проверить исключения

• …

Page 16: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Сборка проекта. Как было раньше.

• MAKEFILES

• Javah

• SWIG

• Ecplise plugin Sequoyah

• …

Page 17: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

New experimental Gradle plugin.

Page 18: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Раз.

Page 19: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Два.

Page 20: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Три.

Page 21: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

Download and enjoy!

https://goo.gl/yn4ZmC

Page 22: Дмитрий Юницкий. «Android NDK или как я перестал бояться и полюбил нативную разработку».

More info.• http://developer.android.com/training/articles/perf-jni.html

• https://developer.android.com/ndk/guides/concepts.html

• http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/design.html

• http://tools.android.com/tech-docs/new-build-system/gradle-experimental

• http://normanmaurer.me/blog/2014/01/07/JNI-Performance-Welcome-to-the-dark-side/

• http://compmus.ime.usp.br/sbcm/2013/pt/docs/pos_tec_4.pdf

• http://janet-project.sourceforge.net/papers/jnibench.pdf