JNI註冊,是指將java層方法(native關鍵字修飾的)和C層方法對應起來,以實現java層代碼調用c層代碼的目的。JNI註冊分爲靜態註冊和動態註冊兩種,靜態註冊是通過固定格式方法名進行關聯,動態註冊是通過動態添加映射關係來進行關聯,方法名可以隨便起,比較靈活,我們推薦使用動態註冊。在進行註冊前,需要先下載兩個工具Clion和eclipse(能寫java application就可以),然後我們就可以開始註冊了。
靜態註冊
- 首先,在eclipse新建一個Java Application,名稱可以隨意,比如叫JavaJni,然後在src目錄下新建一個package名爲clz,再clz包下新建java類Register.java,類中寫一個native方法如下:
package clz;
public class Register {
native String helloworld();
}
- 進入命令行,來到Register.java所在目錄下,使用命令 javac Register.java生成Register.class文件
- 命令行,回到src目錄下,通過命令 javah clz.Register 生成clz_Register.h
- 在Clion中,新建一個C++ Library,Library type選擇shared,並將jdk/include下的jni.h文件和jni_md.h文件拷貝過來
- 將第三步中生成的clz_Register.h文件拷貝到Clion中剛剛新建的項目中
- 修改jni.h的引用如下
#include "jni.h"
- 新建clz_Register.c文件,引入clz_Register.h,實現.h中對應的函數
//clz_Register.c
#include "clz_Register.h"
JNIEXPORT jstring JNICALL Java_clz_Register_helloworld
(JNIEnv * env, jobject jobject){
return (*env)->NewStringUTF(env,"helloworld");
}
- 在Clion項目中的CMakeLists.txt中添加編譯配置
add_library(firstlib SHARED clz_Register.h clz_Register.c)
- 第一個參數firstlib,表示編譯後生成的動態庫名稱
- 第二個參數可以選擇STATIC或者SHARED,分別表示是靜態庫還是動態庫,一般我們使用動態庫
- 第三個及後面的參數,表示需要編譯入庫的文件
- 在Clion中選擇Build-BuildProject,可以在cmake-build-debug下生成libfirstlib.dylib(mac爲dylib,windows爲dll)
- 將第8步生成的libfirstlib.dylib拷貝到eclipse項目的libs目錄下(沒有可新建)
- 在Register.java中加載庫,並且調用庫中函數
package clz;
public class Register {
static {
System.load("/Users/djx/eclipse-workspace/JavaJni/libs/libfirstlib.dylib");
}
native String helloworld();
public static void main(String[] args) {
Register re = new Register();
System.out.println(re.helloworld());
}
}
動態註冊
- 首先,在eclipse新建一個Java Application,名稱可以隨意,比如叫JavaJni,然後在src目錄下新建一個package名爲register,在register包下新建java類DynamicRegister.java,類中寫native方法如下:
package register;
public class DynamicRegister {
native void dynamicFunc1();
native String dynamicFunc2();
}
- 在Clion中,新建一個C++ Library,Library type選擇shared,並將jdk/include下的jni.h文件和jni_md.h文件拷貝過來
- 新建DynamicRegister.c文件,引入jni.h和實現兩個方法,如下
#include "jni.h"
void func1(JNIEnv *env,jobject jobject){
printf("dynamicNative1 動態註冊\n");
}
jstring func2(JNIEnv *env,jobject jobject){
return (*env)->NewStringUTF(env,"hello everybody 2");
}
- 添加動態註冊
static const char * mClassName = "register/DynamicRegister";
// 三個參數,java層函數名,java層方法簽名,c層方法指針
// 獲取簽名方法:javap -s -p DynamicRegister.class
static const JNINativeMethod mMethods[]={
{"dynamicFunc1","()V",(void*)func1},
{"dynamicFunc2","()Ljava/lang/String;",(void*)func2},
};
//java層load時,便會自動調用該方法
JNIEXPORT jint JNICALL
JNI_OnLoad(JavaVM *vm, void *reserved){
printf("hello dynamic0\n");
JNIEnv* env = NULL;
//獲得 JniEnv
int r = (*vm)->GetEnv(vm,(void**) &env, JNI_VERSION_1_8);
if( r != JNI_OK){
return -1;
}
//FindClass,反射,通過類的名字反射出一個
jclass mainActivityCls = (*env)->FindClass( env,mClassName); // 註冊 如果小於0則註冊失敗
r = (*env)->RegisterNatives(env,mainActivityCls,mMethods,2);
if(r != JNI_OK )
{
return -1;
}
printf("hello dynamic\n");
return JNI_VERSION_1_8;
}
- 在Clion項目中的CMakeLists.txt中添加編譯配置
add_library(firstdylib SHARED DynamicRegister.c)
- 第一個參數firstlib,表示編譯後生成的動態庫名稱
- 第二個參數可以選擇STATIC或者SHARED,分別表示是靜態庫還是動態庫,一般我們使用動態庫
- 第三個及後面的參數,表示需要編譯入庫的文件
- 在Clion中選擇Build-BuildProject,可以在cmake-build-debug下生成libfirstdylib.dylib(mac爲dylib,windows爲dll)
- 將第8步生成的libfirstdylib.dylib拷貝到eclipse項目的libs目錄下(沒有可新建)
- 在DynamicRegister.java中加載庫,並且調用庫中函數
package register;
public class DynamicRegister {
native void dynamicFunc1();
native String dynamicFunc2();
static {
System.load("/Users/djx/eclipse-workspace/JavaJni/libs/libfirstdylib.dylib");
}
public static void main(String[] args) {
DynamicRegister dr = new DynamicRegister();
dr.dynamicFunc1();
String result=dr.dynamicFunc2();
System.out.println("dynamicFunc2:"+result+"\n");
}
}
代碼地址
補充知識
- 如何獲取一個Java方法的簽名
- 使用命令行,進入 Xxxx.java 目錄
- 使用 javac Xxxx.java 生成Xxxx.class文件
- 使用 javap -s -p xx.xx.Xxxx.class便可以得到簽名如下
- descriptor:後面的即爲該方法的簽名
- Java和C/C++中的基本類型的映射關係
java類型 | jni類型 | 描述 |
---|---|---|
boolean | jboolean | unsighed 8 bits |
byte | jbyte | sighed 8 bits |
char | jchar | unsighed 16 bits |
short | jshort | sighed 16 bits |
int | jint | sighed 32 bits |
long | jlong | sighed 64 bits |
float | jfloat | sighed 32 bits |
double | jdouble | sighed 64 bits |
Class | jclass | class類對象 |
String | jstring | 字符串對象 |
Object | jobject | 任何java對象 |
byte[] | jbyteArray | byte數組 |