Сергей Комлач
Занимается разработкой под мобильные платформы более 8-ми лет. Последние 2 года занимает должность старшего Android разработчика. Ведет курс разработки по Android в рамках Google Android Study . Докладчик на UAMobile 2014 (Kiev) , Lviv Mobile Developers Day 2014 , Google Developers Fest (2014, Lviv) , MobileOptimized (Minsk). Соорганизатор GDG Kremenchuk .
Kubernetes: від знайомства до використання у CI/CD
Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android
1. Android NDK & JNI
Использование Java Native Interface (JNI) и
кросплатформенных C/C++ реализаций в Java (Android)
Sergey Komlach
GDG Kremenchuk, StickyPassword
Skype: s.komlach
sergey.komlach@stickypassword.com
2. ● Что такое JNI/NDK? Быстродействие, много готовых либ, платформозависимость, не-
"Write once, run anywhere" (WORA), аналогия с Reflection, используемые типы и вызовы
● Настройка окружения (NDK x 2), отладка, поддержка и тестирование всех платформ
● Пример простейшей реализации Java → C.
● Вызов из С++ метода Java как колбека (Java interface)
● Особенности выполнения кода (BlackBerry10 (mktime()), IntelAtom, х64), tmpdir(), флаги
оптимизации, порезаный Bionic и прочие либы в Андроид, удаление SO-шек на Sony при
апдейте
Рассмотрим….
3. Wiki: Java Native Interface (JNI) — стандартный механизм для запуска кода, под управлением
виртуальной машины Java (JVM), который написан на языках С/С++ или Ассемблера, и
скомпонован в виде динамических библиотек, позволяет не использовать статическое
связывание. Это даёт возможность вызова функции С/С++ из программы на Java, и наоборот.
Более ранние интерфейсы, в отличие от JNI, не удовлетворяли условию двоичной
совместимости.
Wiki: В 2009 году в дополнение к ADT был опубликован Android Native Development Kit (NDK) —
пакет инструментариев и библиотек, позволяющий реализовать часть приложения на языке
С/С++. NDK рекомендуется использовать для разработки участков кода, критичных к скорости.
Реально же наиболее частое применение:
Работа с OpenGL ES
Использование кросс-платформенных игровых движков, например Cocos2Dx
Использование уже написанного на C/C++ кода (а его ох как дофига написано!). Часто, это
работа с мультимедиа, например FFMPEG, libpng или наукоемкие вещи типа openCV
«Обход» «бутылочного горлышка» в программе (UP! «тяжелых» процессов)
Кроссплатформенное (повторное) использование кода
JNI/NDK
4. Работа с NDK на порядок усложняет разработку.
- Разработчик должен понимать Java (само собой)
- С/С++ (особенно внимание на память, указатели, треды/семафоры и т.д)
- команды и принципы работы JVM (очень пригодится если вы уже работали с Reflection)
Class cls = sample1.getClass();
try {
cls.getDeclaredMethod("print", String.class).invoke(sample1, "sample class");
cls.getDeclaredMethod("print", String.class).invoke(sample1, "test string");
cls.getDeclaredMethod("print", null).invoke(sample2, null);
cls.getDeclaredMethod("print", null).invoke(sample3, null);
} catch (Exception e) {}
- Нужно учитывать большое количество ограничений JNI в Android (порезаные библиотека,
размеры типов, «пустышки» реализаций)
- Сложная настройка среды и особенно отладка
Таким образом, работа с NDK чаще всего представляем из себя процесс (часто — мучительный)
сборки некой библиотеки и написиние оберток (wrappers) на нативные методы. В тоже время,
сейчас есть возможность ваять приложение практически без использования Java, используя
NativeActivity (API 9 и выше).
5. package com.example;
public class NativeTest{
static {
System.loadLibrary("nativetest"); // libs/armeabi-v7a/libnativetest.so
}
public native boolean testMethod(int arg);
}
JNIEXPORT jboolean JNICALL Java_com_example_NativeTest_testMethod
(JNIEnv *env, jobject caller, jint arg);
JNIEXPORT — необходимый для JNI модификатор. Типы данных с префиксом «j»: jdouble,
jobject, jstring etc — это «отражения» объектов и типов Java в C/C++.
Именование
6. jstring JAVA_JNI_This_1Is_1Native_00024Wrapper_00024_000408_000413_000397(...)
Дело в том, что _1 это аналог нижнего подчёркивания.
_00024 это символ $, то есть может как разделитель внутреннего класса использоваться.
_00408, 0xxxx, это код в юникоде.
В итоге получается:
class JNI {
static class This_Is_Native {
static class Wrapper$ {
static String Юникод(...)
}
Именование, часть 2
7. Java JNI JNI array Code Array Code
boolean jboolean jbooleanArray Z [Z
byte jbyte jbyteArray B [B
char jchar jcharArray C [C
double jdouble jdoubleArray D [D
float jfloat jfloatArray F [F
int jint jintArray I [I
long jlong jlongArray J [J
short jshort jshortArray S [S
Object/Class/
String
jobject/jclass/
jstring
jobjectArray/-/- L/L/L [L/[L/[L
void void - V -
9. ● Устаналивается APK
● Если внутри находятся SO-файлы (аналог DLL) они копируются в
/data/data/apppackage/app_lib/*.so
● При первом обращении к классу, использующему нативные библиотеки, последние
загружаются через System.load(«name»)
● Библиотека «живет» пока не будет завершено приложение
Как работает взаимодействие
между Java и Native
10. ● Качаем NDK. При чем если хотим все платформы, то нужно NDK x32 (для arm6, arm7a, x86
& mips) и NDK x64 (arm8, x86_64 & mips_64 и все х32)
● Устанавливаем окружение
(путь к папке NDK)
● «цепляем» в IDE
С чего начинается NDK
11. javah создает файлы заголовков и исходники C из Java класса.
Эти файлы обеспечивают связь, которая позволит взаимодействовать вашему Java и C коду
javah -classpath bin/classes -jni -d jni com.my.javaclass
javah
14. - Логгирование
__android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
- StackTrace в LogCat
Dalvik:
F/libc (17861): invalid address or address of corrupt block 0x7f51ce50 passed to dlfree
F/libc (17861): Fatal signal 11 (SIGSEGV) at 0xdeadbaad (code=1), thread 29266 (Thread-9488)
ART:
A/art(21149): sart/runtime/check_jni.cc:65] JNI DETECTED ERROR IN APPLICATION: input is not valid Modified UTF-8: illegal
start byte 0xa3
….
A/art(21149): sart/runtime/check_jni.cc:65] native: #07 pc 000bfaad /system/lib/libart.so (art::CheckJNI::NewStringUTF
(_JNIEnv*, char const*)+44)
A/art(21149): sart/runtime/check_jni.cc:65] native: #08 pc 00008fbb /data/app/com.stickypassword.android-
1/lib/arm/libSPCAgent.so (setValue(_jobject*, int, long, char*, char*)+202)
A/art(21149): sart/runtime/check_jni.cc:65] native: #09 pc 00009ac7 /data/app/com.stickypassword.android-
1/lib/arm/libSPCAgent.so (Java_com_spc_SPCManager_GetAuthCredentialsV2+82)
Отладка
15. GDB (GNU Debugger) — переносимый отладчик проекта GNU, который работает на многих UNIX-
подобных системах и умеет производить отладку многих языков программирования, включая
Си, C++, Free Pascal, FreeBASIC, Ada и Фортран. GDB — свободное программное обеспечение,
распространяемое по лицензии GPL.
GDB
16. package com.example.testsimplycall;
public class NativeTest {
static {
try{
// == Runtime.getRuntime().loadLibrary("testSimplyCall");
System.loadLibrary("testSimplyCall");
} catch (UnsatisfiedLinkError err){
//need catch exception
err.printStackTrace();
}
}
public native void testMePlz(String msg);
}