Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.
Android NDK, или как я
перестал бояться и полюбил
нативную разработку.
Android NDK.
• JNI - набор инструментов для запуска кода на C/
C++/Ассемблере из виртуальной машины Java
• Вспомогательная...
Зачем?
• Критичные по производительности участки кода
• Использование написанного ранее кода или
существующих библиотек (O...
Какие возникают
сложности?
• Работа с jni из нативных потоков
• Отладка
• Возрастающий размер результирующей apk
• Локальн...
Базовый пример.
public class IntegrationActivity extends AppCompatActivity

{

…

@Override

protected void onCreate(Bundl...
Типы данных JNI.
#ifdef HAVE_INTTYPES_H

# include <inttypes.h> /* C99 */

typedef uint8_t jboolean; /* unsigned 8 bits
*/...
JNIEnv и функции
JNI.
• JNIEnv
• IsSameObject, Call*Method, Get*Field, Set*Field,
New*Array…
• NewGlobalRef, DeleteGlobalR...
Локальные и глобальные
ссылки.
• Объекты, на которые есть ссылки - не могут быть очищены
GC
• Локальные ссылки - в предела...
Таблица локальных и
глобальных ссылок.
void DumpDalvikReferenceTables()

{

JNIEnv * env = jni::GetEnv();

jclass vm_class...
Нативные потоки.
• AttachCurrentThread для работы с jni
• DetachCurrentThread перед завершением
• Локальные ссылки НЕ очищ...
Нативные потоки.
FindClass.
• Простой вариант решения - кешировать класс в
JNI_OnLoad.
• Более сложный, но гибкий - кеширо...
Кеширование ClassLoader.
JNIEXPORT jint JNICALL

JNI_OnLoad(JavaVM *jvm, void *) {

g_jvm = jvm;



JNIEnv *env = jni::Get...
Кеширование jClass,
jMethodId, jFieldId.• jClass -локальные ссылки, для кеширования
необходимо преобразовывать в глобальны...
Кеширование jClass,
jMethodId, jFieldId.• jClass -локальные ссылки, для кеширования
необходимо преобразовывать в глобальны...
Запуск native метода из
Java.
• Создать новый стекфрейм
• Передать аргументы согласно ABI
• Передать JNIEnv* и jclass(jobj...
Сборка проекта. Как было
раньше.
• MAKEFILES
• Javah
• SWIG
• Ecplise plugin Sequoyah
• …
New experimental Gradle
plugin.
Раз.
Два.
Три.
Download and enjoy!
https://goo.gl/yn4ZmC
More info.
• http://developer.android.com/training/articles/perf-jni.html
• https://developer.android.com/ndk/guides/conce...
Upcoming SlideShare
Loading in …5
×

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

1,544 views

Published on

Дмитрий рассказал о преимуществах, недостатках и основных сценариях использования нативного кода при написании кроссплатформенных приложений. Спикер рассказывает о разнообразных подводных камнях, которые неизбежно возникают при работе с NDK, и показывает интересные хаки и неочевидные возможности решения возникающих проблем.

Published in: Software
  • Be the first to comment

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

  1. 1. Android NDK, или как я перестал бояться и полюбил нативную разработку.
  2. 2. Android NDK. • JNI - набор инструментов для запуска кода на C/ C++/Ассемблере из виртуальной машины Java • Вспомогательная нативная библиотека(скомпилированная для каждой поддерживаемой ABI) или полностью нативное приложение используя NativeActivity • Java -> нативный код, нативный код -> Java, Java -> нативный код -> Java…
  3. 3. Зачем? • Критичные по производительности участки кода • Использование написанного ранее кода или существующих библиотек (OpenCV, ffmpeg, …) • Написание кроссплатформенного кода для нескольких платформ(бизнес-логика, алгоритмы и тд)
  4. 4. Какие возникают сложности? • Работа с jni из нативных потоков • Отладка • Возрастающий размер результирующей apk • Локальные/глобальные ссылки • Дорогой переход из java кода в нативный (и обратно) • Прочие приятные сюрпризы от jni)
  5. 5. Базовый пример. 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 JNICALL
 Java_my_test_integration_IntegrationActivity_nativeGetBooleanValue(JNIEnv *env, jobject instance) {
 SomeCppClass object;
 return (jboolean) object.getSomeValue();
 }
 
 } // extern "C" Java C++
  6. 6. Типы данных 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 */
 #else
 typedef 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; Примитивные Ссылочный
  7. 7. JNIEnv и функции JNI. • JNIEnv • IsSameObject, Call*Method, Get*Field, Set*Field, New*Array… • NewGlobalRef, DeleteGlobalRef, DeleteLocalRef • AttachCurrentThread, DetachCurrentThread • JNI_OnLoad, JNI_OnUnload
  8. 8. Локальные и глобальные ссылки. • Объекты, на которые есть ссылки - не могут быть очищены GC • Локальные ссылки - в пределах жизни метода в рамках одного потока • Java VM автоматически очищает ссылки при возврате из нативного метода • Есть предел на количество создаваемых локальных ссылок • Глобальные ссылки действительны вплоть до явного освобождения
  9. 9. Таблица локальных и глобальных ссылок. 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);
 }
  10. 10. Нативные потоки. • AttachCurrentThread для работы с jni • DetachCurrentThread перед завершением • Локальные ссылки НЕ очищаются - нужно всегда(!) очищать все созданные ссылки с помощью DeleteLocalRef либо создавать отдельный пул ссылок при входе в метод через PushLocalFrame/PopLocalFrame • FindClass НЕ работает
  11. 11. Нативные потоки. FindClass. • Простой вариант решения - кешировать класс в JNI_OnLoad. • Более сложный, но гибкий - кешировать сам ClassLoader.
  12. 12. Кеширование ClassLoader. JNIEXPORT jint JNICALL
 JNI_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;
  13. 13. Кеширование jClass, jMethodId, jFieldId.• jClass -локальные ссылки, для кеширования необходимо преобразовывать в глобальные. • jMethodId, jFieldId - просто структуры, можно записывать без преобразования в статические или глобальные переменные и использовать в любых потоках.
  14. 14. Кеширование jClass, jMethodId, jFieldId.• jClass -локальные ссылки, для кеширования необходимо преобразовывать в глобальные. • jMethodId, jFieldId - просто структуры, можно записывать без преобразования в статические или глобальные переменные и использовать в любых потоках. • В некоторых случаях работа через jni может быть ЗНАЧИТЕЛЬНО медленнее аналогичного кода на Java.
  15. 15. Запуск native метода из Java. • Создать новый стекфрейм • Передать аргументы согласно ABI • Передать JNIEnv* и jclass(jobject) • Synchronized • Проверить исключения • …
  16. 16. Сборка проекта. Как было раньше. • MAKEFILES • Javah • SWIG • Ecplise plugin Sequoyah • …
  17. 17. New experimental Gradle plugin.
  18. 18. Раз.
  19. 19. Два.
  20. 20. Три.
  21. 21. Download and enjoy! https://goo.gl/yn4ZmC
  22. 22. 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

×