最近工作中在java工程中通過jni調(diào)用c程序編譯的庫進行加解密,總結(jié)一下jni的用法
環(huán)境:macos 10.12.6,jdk7
- 編寫一個包含native方法的java文件
System.load()和System.loadLibrary()的區(qū)別可以看這里,簡單來說load是從指定位置加載一個單獨的庫文件(參數(shù)是庫文件的全路徑),loadLibrary是從java.library.path加載庫文件(參數(shù)是文件名,不包括擴展名,macos會自動找lib+文件名+.dylib)。如果庫文件依賴別的庫文件,load需要自己再引入,loadLibrary可以自動在java.library.path找到并引入。可以通過
System.getProperty("java.library.path")
查看java.library.path路徑是什么,因為loadLibrary對不同平臺的規(guī)則不一樣,所以還是使用load好一點,對名字和文件位置沒有要求
package com.lfz;
import java.io.File;
public class Main {
static {
System.load(Main.class.getResource(File.separator).getPath()+"libhelloJNI.dylib");
}
public static void main(String[] args) {
System.out.println(new Main().max(7, 5));
}
public native int max(int a, int b);
}
- 編譯java文件得到class文件
因為我使用的ide自動就會在classpath下編譯java源文件,所以不需要使用javac命令手動編譯
- 調(diào)用javah命令生成c程序頭文件
進入classpath下執(zhí)行javah命令,注意是后面是完整類名(包名+類名),得到
com_lfz_Main.h
文件
cd $CLASS_PATH
javah com.lfz.Main
- 在和
com_lfz_Main.h
同一目錄下,編輯c文件(名字可以隨便取)
c程序頭文件引入<>和""區(qū)別,可以看這里,簡單來說<>是從系統(tǒng)默認(rèn)的位置找頭文件,""是先從當(dāng)前位置找再去系統(tǒng)默認(rèn)的地方找
#include <jni.h> //引入jni的頭文件
#include "com_lfz_Main.h" //引入剛才產(chǎn)生的h文件
#include <stdio.h>
// 返回值類型jint對應(yīng)java的int類型,方法名規(guī)則:Java_包名_類名_方法名
// 方法參數(shù)前兩個是固定的,之后對應(yīng)java native方法中的參數(shù)類型
JNIEXPORT jint JNICALL Java_com_lfz_Main_max(JNIEnv *env,jobject obj,jint a,jint b){
return a>b?a:b;
}
- 使用gcc編譯c源文件得到庫文件
-dynamiclib代表生成動態(tài)庫,-I /System/Library/Frameworks/JavaVM.framework/Headers代表添加額外的頭文件路徑(macos的jni.h在這里,別的os需要修改這里),-o libhelloJNI.dylib代表最后編譯出的庫文件名
gcc -dynamiclib -I /System/Library/Frameworks/JavaVM.framework/Headers helloJNI.c -o libhelloJNI.dylib
對于引用類型則有:jobject, jstring, jthrowable, jclass, jarray, 以及繼承于jarray,對應(yīng)于其原生類型的8種jarray和jobjectarray。