# 猴子也看不懂的 JNI --- # Outline * Java call C * C call Java * Settings * cmake * Troubles * Tip --- # Java call C ---- ## Simple JNI ``` java package com.maxwin.dab.jni; public class Lib { public native void hello(); } ``` ``` c Java_com_maxwin_dab_jni_Lib_hello(JNIEnv* env, jobject thiz); ``` ---- ## Rule `JAVA_` + `package name` + `class name` + `func name` ---- ## Add some args ``` java public native void arg1(int a); public native void arg2(int a, int b); ``` ``` c void Java_com_maxwin_dab_jni_Lib_arg1( JNIEnv* env, jobject thiz, jint a); void Java_com_maxwin_dab_jni_Lib_arg2( JNIEnv* env, jobject thiz, jint a, jint b); ``` ---- ## Type 對應 Table |Java type|C type|sig| |:-------:|:----:|:-:| |boolean|jboolean|Z| |byte|jbyte|B| |char|jchar|C| |short|jshort|S| |int|jint|I| |long|jlong|J| ---- ## Type 對應 Table conti. sig 晚點解釋 |Java type|C type|sig| |:-------:|:----:|:-:| |float|jfloat|F| |double|jdouble|D| |Any Object|jobject|見下| --- # C call Java > 透過 Java Reflection ---- ## 大部分使用方式 * `CallStatic<type>Method` * `Call<type>Method` * `SetStatic<type>Field` * `Set<type>Field` * `GetStatic<type>Field` * `Get<type>Field` ---- ## 實際使用時會遇到的(init) ``` java package com.maxwin.tw; class Yume { } ``` ``` c jclass clz = env->FindClass("com/maxwin/tw/Yume"); jmethodID init = env->GetMethodID(clz, "<init>", "()V"); jobject yume = env->NewObject(clz, init); // ... env->DeleteLocalRef(yume); env->DeleteLocalRef(clz); ``` ``` java new Yume(); ``` ---- ## 實際使用時會遇到的(set value) ``` java package com.maxwin.tw; class Yume { int dreamCounts; } ``` ``` c jfieldID dreamCountsField = env->GetFieldID( clz, "dreamCounts", "I"); env->SetIntField(yume, dreamCountsField, 101); ``` ---- ## 實際使用時會遇到的(call func) ``` java package com.maxwin.tw; class Yume { int dream(int a, int b) { return a + b; } } ``` ``` c jmethodID dreamMethod = env->GetMethodID( clz, "dream", "(II)I"); jint dreams = env->CallIntMethod(yume, dreamMethod, 50, 51); ``` ---- ## 實際上最長使用到的 ``` c jfieldID GetFieldID( jclass clazz, const char *name, const char *sig) jmethodID GetMethodID( jclass clazz, const char *name, const char *sig) ``` ---- ## `const char *sig`?, Signature? * Field sig * Method sig ---- ## Type VS Sig Rule (Adv) ---- ## array sig [ + type sig byte -> B -> Array + B = [B ``` java byte[]; ``` ``` c "[B" ``` ---- ## object sig L + full class name + ; ``` java String; // java.lang.String ``` ``` c "Ljava/lang/String;" ``` ---- ## function sig ( + args sig + ) + return sig ``` java void foo(int a, int b, int c) {} ``` ``` c "(III)V" ``` ---- ## Type VS Sig Table (Adv) |Java type|sig| |:-------:|:-:| |void|V| |void foo()|()V| |byte[]|[B| |java.lang.String|Ljava/lang/String;| ---- ## More Prac I ``` java int example1(int a, int b) {/*...*/} ``` ``` c "(II)I" ``` ---- ## More Prac II ``` java void example2(String a[]) {/*...*/} ``` ``` c "([Ljava/lang/String;)V" ``` --- # Setting `app/build.gradle` ``` gradle apply plugin: 'com.android.application' android { externalNativeBuild { cmake { path "src/main/jni4/CMakeLists.txt" } } sourceSets { main { jniLibs.srcDirs = ['src/main/jni4/jniLibs'] } } flavorDimensions 'cpuArch' productFlavors { arm { ndk { abiFilter 'armeabi' } } } // ... } ``` ---- ## C Hello World [CMakeLists.txt](https://github.com/googlesamples/android-ndk/blob/master/hello-jni/app/src/main/cpp/CMakeLists.txt) 產生 `libhello.so` ``` cmake cmake_minimum_required(VERSION 3.4.1) add_library( hello SHARED hello-jni.c ) # Include libraries needed for hello-jni lib target_link_libraries( hello android log ) ``` ---- ## Java 端 [HelloJni.java](https://github.com/googlesamples/android-ndk/blob/29fa1bbae83eecf8c21dfeb83b12878fbe7f6595/hello-jni/app/src/main/java/com/example/hellojni/HelloJni.java#L58-L60) ``` java public class Lib { static { System.loadLibrary("hello"); } } ``` --- # 現實的 cmake ``` cmake cmake_minimum_required(VERSION 3.4.1) find_library( log-lib log ) include_directories( inc ) set(distribution_DIR ${CMAKE_SOURCE_DIR}/jniLibs/${ANDROID_ABI}) add_library(dabforex SHARED IMPORTED) set_target_properties(dabforex PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/libdablibforexpartner.so) add_library(dabforamaryllo SHARED IMPORTED) set_target_properties(dabforamaryllo PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/libdabcontroller.so) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -fexceptions") # lib xxx .so add_library( dabcontrollerdemo SHARED demoMain.cpp Yume+LEB128.cpp Yume+Decoder.cpp Yume+TPEG.cpp Yume+JavaCallback.cpp Yume+JNIInit.cpp YumeJNI+ObjectAccess.cpp ) # Include libraries needed for hello-jni lib target_link_libraries( dabcontrollerdemo dabforamaryllo dabforex android ${log-lib} ) ``` ---- ## Proj layout ``` sh ~/dab/app/src/main/jni4> tree . ├── CMakeLists.txt ├── xxx.cpp ├── inc │ └── API.h └── jniLibs └── armeabi ├── libdabcontroller.so └── libdablibforexpartner.so ``` ---- 限制 cmake 最低版本為 3.4.1 ``` cmake cmake_minimum_required(VERSION 3.4.1) ``` ---- 確保有 log lib log-lib=log ``` cmake find_library( log-lib log ) ``` ---- 把 inc 加入到 header search path ``` cmake include_directories( inc ) ``` ---- ## ANDROID_ABI 不同手機 cpu abi 不同 ``` cmake # distribution_DIR=${CMAKE_SOURCE_DIR}/jniLibs/${ANDROID_ABI} set(distribution_DIR ${CMAKE_SOURCE_DIR}/jniLibs/${ANDROID_ABI}) ``` ---- ## 加入已編好的 .so ``` cmake add_library(dabforex SHARED IMPORTED) set_target_properties(dabforex PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/libdablibforexpartner.so) add_library(dabforamaryllo SHARED IMPORTED) set_target_properties(dabforamaryllo PROPERTIES IMPORTED_LOCATION ${distribution_DIR}/libdabcontroller.so) ``` ---- ## java setting ``` java package com.maxwin.dab.jni; public class Lib { static { System.loadLibrary("dablibforexpartner"); System.loadLibrary("dabcontroller"); System.loadLibrary("dabcontrollerdemo"); } } ``` --- # Troubles ---- ## Local Reference `object` 相關類型會有產生 local ref 目前遇到的上限為 `512` 超過便會發生 `JNI ERROR` ``` c // 需要在適當時機處理掉 env->DeleteLocalRef(ref); ``` ---- ## Reference Type(會產生 local ref) * object * string * array * throwable * class ---- ## Value Type 基本型態以及 ``` c struct _jfieldID; /* opaque structure */ typedef struct _jfieldID* jfieldID; /* field IDs */ struct _jmethodID; /* opaque structure */ typedef struct _jmethodID* jmethodID; /* method IDs */ ``` ---- ## Global Reference 一樣要在適當時機釋放 ref 使用時機多為需要 長期/多次 使用 ``` c newRef = env->NewGlobalRef(ref); env->DeleteGlobalRef(newRef); ``` ---- ## pthread interact java main thread ``` c vm->AttachCurrentThread(&env, NULL); // ... vm->DetachCurrentThread(); ``` --- # Tip ---- ## onload & onunload 可以在這兩個時機點 準備/釋放 globol ref ``` c JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) { JNIEnv* env; if (vm->GetEnv((void**)&env, JNI_VERSION_1_6) != JNI_OK) { return JNI_ERR; // JNI version not supported. } ctx.vm = vm; ctx.env = env; return JNI_VERSION_1_6; } JNIEXPORT void JNICALL JNI_OnUnload(JavaVM* vm, void* reserved) { } ``` --- # 欠缺資料(應該) * Exception * Generic * ... --- # 參考資料 * http://www.cnblogs.com/oxgen/tag/Android%20JNI/ * https://www.ibm.com/developerworks/cn/java/j-jni/ * https://mccxj.github.io/blog/20151028_android-ndk-jni.html --- # NO Q & NO A 沒有問題就不用回答 --- ### Test [Android NDK单元测试](https://www.jianshu.com/p/e5270ba1c11d) [UnsatisfiedLinkError: Using JNI to do native calls from Java](http://www.linuxonly.nl/docs/15/15_UnsatisfiedLinkError_Using_JNI_to_do_native_calls_from_Java.html) ---- #### 結論 1. androidTest 2. 自編 * System.load(file.getAbsolutePath()); * System.setProperty("java.library.path", libraryPath); --- # END
{"metaMigratedAt":"2023-06-14T17:54:48.158Z","metaMigratedFrom":"YAML","title":"猴子也看不懂的 JNI","breaks":true,"description":"JNI 簡報","lang":"zh","dir":"ltr","robots":"index, follow","contributors":"[{\"id\":\"6883ab5f-8423-424e-bfcb-d0002f96698f\",\"add\":9573,\"del\":1153}]"}
    617 views