JNI靜態註冊與動態註冊詳解

JNI註冊,是指將java層方法(native關鍵字修飾的)和C層方法對應起來,以實現java層代碼調用c層代碼的目的。JNI註冊分爲靜態註冊和動態註冊兩種,靜態註冊是通過固定格式方法名進行關聯,動態註冊是通過動態添加映射關係來進行關聯,方法名可以隨便起,比較靈活,我們推薦使用動態註冊。在進行註冊前,需要先下載兩個工具Clion和eclipse(能寫java application就可以),然後我們就可以開始註冊了。

靜態註冊

  1. 首先,在eclipse新建一個Java Application,名稱可以隨意,比如叫JavaJni,然後在src目錄下新建一個package名爲clz,再clz包下新建java類Register.java,類中寫一個native方法如下:
package clz;
public class Register {
	native String helloworld();
}
  1. 進入命令行,來到Register.java所在目錄下,使用命令 javac Register.java生成Register.class文件
  2. 命令行,回到src目錄下,通過命令 javah clz.Register 生成clz_Register.h
  3. 在Clion中,新建一個C++ Library,Library type選擇shared,並將jdk/include下的jni.h文件和jni_md.h文件拷貝過來
  4. 將第三步中生成的clz_Register.h文件拷貝到Clion中剛剛新建的項目中
  5. 修改jni.h的引用如下
#include "jni.h"
  1. 新建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");
}
  1. 在Clion項目中的CMakeLists.txt中添加編譯配置
add_library(firstlib SHARED clz_Register.h clz_Register.c)
  • 第一個參數firstlib,表示編譯後生成的動態庫名稱
  • 第二個參數可以選擇STATIC或者SHARED,分別表示是靜態庫還是動態庫,一般我們使用動態庫
  • 第三個及後面的參數,表示需要編譯入庫的文件
  1. 在Clion中選擇Build-BuildProject,可以在cmake-build-debug下生成libfirstlib.dylib(mac爲dylib,windows爲dll)
  2. 將第8步生成的libfirstlib.dylib拷貝到eclipse項目的libs目錄下(沒有可新建)
  3. 在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());
	}
}

動態註冊

  1. 首先,在eclipse新建一個Java Application,名稱可以隨意,比如叫JavaJni,然後在src目錄下新建一個package名爲register,在register包下新建java類DynamicRegister.java,類中寫native方法如下:
package register;
public class DynamicRegister {
	native void dynamicFunc1();
	native String dynamicFunc2();
}
  1. 在Clion中,新建一個C++ Library,Library type選擇shared,並將jdk/include下的jni.h文件和jni_md.h文件拷貝過來
  2. 新建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");
}
  1. 添加動態註冊
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;
}
  1. 在Clion項目中的CMakeLists.txt中添加編譯配置
add_library(firstdylib SHARED DynamicRegister.c)
  • 第一個參數firstlib,表示編譯後生成的動態庫名稱
  • 第二個參數可以選擇STATIC或者SHARED,分別表示是靜態庫還是動態庫,一般我們使用動態庫
  • 第三個及後面的參數,表示需要編譯入庫的文件
  1. 在Clion中選擇Build-BuildProject,可以在cmake-build-debug下生成libfirstdylib.dylib(mac爲dylib,windows爲dll)
  2. 將第8步生成的libfirstdylib.dylib拷貝到eclipse項目的libs目錄下(沒有可新建)
  3. 在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");
	}
}

代碼地址

github地址請點擊

補充知識

  • 如何獲取一個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數組
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章