owned this note
owned this note
Published
Linked with GitHub
---
title: 猴子也看不懂的 JNI
description: JNI 簡報
tags: FFI, java, android, jni, ndk
# noindex index
# nofollow follow
robots: index, follow
lang: zh
dir: ltr
breaks: true
# GA: UA-12345667-8
# disqus: hackmd
# slideOptions:
# transition: fade
# theme: white
---
# 猴子也看不懂的 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