23
Android NDK & JNI Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java (Android) Sergey Komlach GDG Kremenchuk, StickyPassword Skype: s.komlach [email protected]

Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

Embed Size (px)

Citation preview

Page 1: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

Android NDK & JNIИспользование Java Native Interface (JNI) и

кросплатформенных C/C++ реализаций в Java (Android)

Sergey KomlachGDG Kremenchuk, StickyPassword

Skype: [email protected]

Page 2: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

● Что такое JNI/NDK? Быстродействие, много готовых либ, платформозависимость, не- "Write once, run anywhere" (WORA), аналогия с Reflection, используемые типы и вызовы

● Настройка окружения (NDK x 2), отладка, поддержка и тестирование всех платформ

● Пример простейшей реализации Java → C.

● Вызов из С++ метода Java как колбека (Java interface)

● Особенности выполнения кода (BlackBerry10 (mktime()), IntelAtom, х64), tmpdir(), флаги оптимизации, порезаный Bionic и прочие либы в Андроид, удаление SO-шек на Sony при апдейте

Рассмотрим….

Page 3: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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

Page 4: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

Работа с 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 и выше).

Page 5: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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++.

Именование

Page 6: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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

Page 7: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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 -

Page 8: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

Внутренности JNI в Android

Page 9: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

● Устаналивается APK

● Если внутри находятся SO-файлы (аналог DLL) они копируются в /data/data/apppackage/app_lib/*.so

● При первом обращении к классу, использующему нативные библиотеки, последние загружаются через System.load(«name»)

● Библиотека «живет» пока не будет завершено приложение

Как работает взаимодействие между Java и Native

Page 10: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

● Качаем NDK. При чем если хотим все платформы, то нужно NDK x32 (для arm6, arm7a, x86 & mips) и NDK x64 (arm8, x86_64 & mips_64 и все х32)

● Устанавливаем окружение (путь к папке NDK)

● «цепляем» в IDE

С чего начинается NDK

Page 11: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

javah создает файлы заголовков и исходники C из Java класса. Эти файлы обеспечивают связь, которая позволит взаимодействовать вашему Java и C коду

javah -classpath bin/classes -jni -d jni com.my.javaclass

javah

Page 12: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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

Page 13: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android
Page 14: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

- Логгирование __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)

Отладка

Page 15: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

GDB (GNU Debugger) — переносимый отладчик проекта GNU, который работает на многих UNIX-подобных системах и умеет производить отладку многих языков программирования, включая Си, C++, Free Pascal, FreeBASIC, Ada и Фортран. GDB — свободное программное обеспечение, распространяемое по лицензии GPL.

GDB

Page 16: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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);

}

Page 17: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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);

}

Page 18: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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);

}

Page 19: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

/* 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

Page 20: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

#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;

}

Page 21: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

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);

}

Page 22: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

● Blackberry10 (mktime)● ARM & Intel● NDK X32 & х64 ● Android L & 5.0 (копирование массивов)● tmpdir()/tmpfile()● флаги оптимизации ( -03,-Ofast, -SSE etc)● порезаный Bionic и прочие либы в Андроид● *.so на Sony● Проблема 01.01.2037

Грабли

Page 23: Использование Java Native Interface (JNI) и кросплатформенных C/C++ реализаций в Java/Android

Android NDK & JNIИспользование Java Native Interface (JNI) и

кросплатформенных C/C++ реализаций в Java (Android)

Q/A

Sergey KomlachGDG Kremenchuk, StickyPassword

Skype: [email protected]