Upload
studio-stfalconcom
View
65
Download
8
Embed Size (px)
Citation preview
Android NDK & JNIИспользование Java Native Interface (JNI) и
кросплатформенных C/C++ реализаций в Java (Android)
Sergey KomlachGDG Kremenchuk, StickyPassword
Skype: [email protected]
● Что такое JNI/NDK? Быстродействие, много готовых либ, платформозависимость, не- "Write once, run anywhere" (WORA), аналогия с Reflection, используемые типы и вызовы
● Настройка окружения (NDK x 2), отладка, поддержка и тестирование всех платформ
● Пример простейшей реализации Java → C.
● Вызов из С++ метода Java как колбека (Java interface)
● Особенности выполнения кода (BlackBerry10 (mktime()), IntelAtom, х64), tmpdir(), флаги оптимизации, порезаный Bionic и прочие либы в Андроид, удаление SO-шек на Sony при апдейте
Рассмотрим….
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
Работа с 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 и выше).
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++.
Именование
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
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 -
Внутренности JNI в Android
● Устаналивается APK
● Если внутри находятся SO-файлы (аналог DLL) они копируются в /data/data/apppackage/app_lib/*.so
● При первом обращении к классу, использующему нативные библиотеки, последние загружаются через System.load(«name»)
● Библиотека «живет» пока не будет завершено приложение
Как работает взаимодействие между Java и Native
● Качаем NDK. При чем если хотим все платформы, то нужно NDK x32 (для arm6, arm7a, x86 & mips) и NDK x64 (arm8, x86_64 & mips_64 и все х32)
● Устанавливаем окружение (путь к папке NDK)
● «цепляем» в IDE
С чего начинается NDK
javah создает файлы заголовков и исходники C из Java класса. Эти файлы обеспечивают связь, которая позволит взаимодействовать вашему Java и C коду
javah -classpath bin/classes -jni -d jni com.my.javaclass
javah
Application.mk
APP_PLATFORM := android-9
APP_STL := stlport_static
APP_ABI := all32
APP_CPPFLAGS += -std=c++11
Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_CPP_FEATURES += rtti # Enable exceptions in Android.mk
LOCAL_CPP_FEATURES += exceptions # Enable exceptions in Android.mk
LOCAL_LDLIBS := -llog -lz
LOCAL_MODULE := nativeTest
LOCAL_CFLAGS := -DANDROID -O3 -pipe
LOCAL_CXXFLAGS := -DANDROID -O3 -pipe
LOCAL_SRC_FILES :=com_test.cpp
Android.mk & Application.mk
- Логгирование __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
- StackTrace в LogCatDalvik:F/libc (17861): invalid address or address of corrupt block 0x7f51ce50 passed to dlfreeF/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)
Отладка
GDB (GNU Debugger) — переносимый отладчик проекта GNU, который работает на многих UNIX-подобных системах и умеет производить отладку многих языков программирования, включая Си, C++, Free Pascal, FreeBASIC, Ada и Фортран. GDB — свободное программное обеспечение, распространяемое по лицензии GPL.
GDB
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);
}
project/jni/com_example_testsimplycall_NativeTest.h/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#ifndef _Included_com_example_testsimplycall_NativeTest
#define _Included_com_example_testsimplycall_NativeTest
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_com_example_testsimplycall_NativeTest_testMePlz(JNIEnv *env,jobject jobj, jstring msg);
#ifdef __cplusplus
}
#endif
#endif
project/jni/com_example_testsimplycall_NativeTest.cpp#include <def.h>
#include <jni.h>
#include "com_example_testsimplycall_NativeTest.h"
JNIEXPORT void JNICALL Java_com_example_testsimplycall_NativeTest_testMePlz(JNIEnv *env,
jobject jobj, jstring msg){
jboolean isCopy;
const char * Str;
Str = env->GetStringUTFChars(msg, &isCopy);
LOGI("string = \"%s\"",Str);
}
package com.example.testcallback;
import android.util.Log;
public interface NativeCallback {
public void print(String str);
}
public class NativeTest {
static {
try{
System.loadLibrary("testCallback");
} catch (UnsatisfiedLinkError err){
err.printStackTrace();
}
}
public NativeCallback nativecallback = new NativeCallback(){
@Override
public void print(String str) {
Log.d("JAVA_CALLBACK", str);
}};
public native void testMePlz(String msg);
public native void SetListener(NativeCallback javacallback);
}
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#ifndef _Included_com_example_testcallback_NativeTest
#define _Included_com_example_testcallback_NativeTest
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_com_example_testcallback_NativeTest_testMePlz(JNIEnv *env,
jobject jobj, jstring msg);
JNIEXPORT void JNICALL Java_com_example_testcallback_NativeTest_SetListener(
JNIEnv *env, jobject jobj, jobject callback);
#ifdef __cplusplus
}
#endif
#endif
#include <jni.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "com_example_testcallback_NativeTest.h"
jobject javaCallback;
JavaVM* mJVM;
/* Reference to Java-object*/
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
{
mJVM = jvm;
return JNI_VERSION_1_2;
}
JNIEnv* getJniEnv() {
JavaVMAttachArgs attachArgs;
attachArgs.version = JNI_VERSION_1_2;
attachArgs.name = ">>>NativeThread__Any";
attachArgs.group = NULL;
JNIEnv* env;
if (mJVM->AttachCurrentThread(&env, &attachArgs) != JNI_OK) {
env = NULL;
}
return env;
}
void printToLogcat(const char* msg) {
JNIEnv* pEnv = getJniEnv();
jclass javaClass = pEnv->GetObjectClass(javaCallback);
if (javaClass != NULL) {
jmethodID javaMethodID = pEnv->GetMethodID(javaClass, "print","(Ljava/lang/String;)V");
jstring logmsg = pEnv->NewStringUTF(msg);
if (logmsg != NULL) {
pEnv->CallVoidMethod(javaCallback, javaMethodID, logmsg);
pEnv->DeleteLocalRef(logmsg);
logmsg = NULL;
}
pEnv->DeleteLocalRef(javaClass);
javaClass = NULL;
}
}
JNIEXPORT void JNICALL Java_com_example_testcallback_NativeTest_SetListener(
JNIEnv *env, jobject jobj, jobject callback) {
if(javaCallback)
env->DeleteGlobalRef(javaCallback);
javaCallback = env->NewGlobalRef(callback);
}
JNIEXPORT void JNICALL Java_com_example_testcallback_NativeTest_testMePlz(JNIEnv *env,jobject jobj, jstring msg){
jboolean isCopy;
const char * Str;
Str = env->GetStringUTFChars(msg, &isCopy);
printToLogcat(Str);
}
● Blackberry10 (mktime)● ARM & Intel● NDK X32 & х64 ● Android L & 5.0 (копирование массивов)● tmpdir()/tmpfile()● флаги оптимизации ( -03,-Ofast, -SSE etc)● порезаный Bionic и прочие либы в Андроид● *.so на Sony● Проблема 01.01.2037
Грабли
Android NDK & JNIИспользование Java Native Interface (JNI) и
кросплатформенных C/C++ реализаций в Java (Android)
Q/A
Sergey KomlachGDG Kremenchuk, StickyPassword
Skype: [email protected]