clion 引用dll_用CLion实现本地方法并给java调用
眾所周知,PHP是世界上最好的語言,java排第二,因為PHP無所不能。但是在某些場景下java還要調(diào)用本地方法來提高執(zhí)行的效率,故java只能排第二。java提供了jni(Java Native Interface)來實現(xiàn)在java中調(diào)用本地方法。本地方法在java中用native關(guān)鍵字標識,它是一種和機器有關(guān)的方法,一般用C或C++實現(xiàn),而本地方法不是跨平臺的,不同的平臺需要重新編譯。jdk中就有不少地方用了native方法,比如Object類中的hashCode方法:
public native int hashCode();
下面開始使用jni:
(一)創(chuàng)建一個帶有native方法的類
package com.example.jni;
public class JNIObject {
private String name;
public JNIObject(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
public int add(int param1, int param2) {
return param1 + param2;
}
public int sub(int param1, int param2) {
return param1 - param2;
}
public native int multi(int param1, int param2);
public native int div(int param1, int param2);
}
我們假定加減法執(zhí)行效率高可以直接用java實現(xiàn),而乘除法比較慢,需要用C語言來實現(xiàn)。寫好了類我們先編譯,把java文件編譯成class文件,然后再用javah命令生成c頭文件,執(zhí)行javah命令時要注意,我們需要先把當(dāng)前的工作目錄切換到class所在的根目錄,就是包的第一級目錄所在的目錄。比如我們的包名是com.example.jni,那么我們需要切換到com目錄所在的目錄,執(zhí)行的命令格式是javah [-option] 包名.類名
javah -jni com.example.jni.JNIObject
成功后會在當(dāng)前的工作目錄生成一個.h的文件(com_example_jni_JNIObject.h),到此我們就得到了本地方法的接口了,如果有c程序員,可以讓他們實現(xiàn),否則看第2步。
(二)在CLion中實現(xiàn)native方法
如果沒有CLion先請自行安裝。
創(chuàng)建一個C Library項目,填好路徑和項目名,Library type選擇shared,Language standard是指c語言的不同標準,類似于我們的jdk的版本,如果你不是c程序員,直接用默認的C99標準就好了
設(shè)置編譯環(huán)境
如果你使用過Visual Studio的話,你可能安裝之后直接創(chuàng)建項目就能寫代碼了,但是CLion有點不同,它只是一個開發(fā)集成環(huán)境,只提供了構(gòu)建工具cmake,并沒有提供編譯器(Visual Studio全套都提供好了),這可以讓開發(fā)者自由去選擇自己喜歡的編譯器,我們打開File->Settings,并選中Build,Execution,Deployment下的Toolchains
可以看到CLion列出了兩個常用的編譯器MinGW和Cygwin,這里我們使用MinGW作為我們的編譯器。需要注意的是如果你的jdk是64位的,那么也要選擇64位的MinGW,不然在調(diào)用的時候會出錯,下面是windows下的MinGW64位下載地址
http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting%20Win32/Personal%20Builds/mingw-builds/installer/mingw-w64-install.exe/download
下載后直接安裝就行了,安裝成功后在上圖中的Environment選擇MinGW的home目錄,然后下面的C、C++編譯器還有調(diào)試器都會根據(jù)選擇的目錄自動找到相關(guān)工具,然后點OK就完成了我們的編譯器的設(shè)置。
實現(xiàn)native方法
我們先把第一步生成的c頭文件(com_example_jni_JNIObject.h)復(fù)制到CLion項目中,這時在打開的com_example_jni_JNIObject.h文件頂部出現(xiàn)一行提示:This file dose not belong to any project target, code insight features might not work properly. 這時我們打開CMakeLists.txt文件,在add_library中加入我們的頭文件,完成后點擊提示的reload changes,完成后的CMakeLists.txt的內(nèi)容如下:
cmake_minimum_required(VERSION 3.15)
project(jni C)
set(CMAKE_C_STANDARD 99)
add_library(jni SHARED library.c library.h com_example_jni_JNIObject.h)
再切換到com_example_jni_JNIObject.h可以發(fā)現(xiàn)剛才的警告已經(jīng)消失了,但是第二行的#include 報錯了,這時因為MinGW編譯器沒有jni.h這個頭文件,打開JDK的home目錄,在include目錄中可以找到j(luò)ni.h頭文件,除此之外,我們還需要include/win32目錄下的jni_md.h頭文件,一共兩個,把這兩個頭文件都復(fù)制到MinGW安裝目錄(就是設(shè)置編譯環(huán)境時的那個Environment的值)下的x86_64-w64-mingw32中的include目錄中,注意這兩個頭文件是一起放在MinGW的這個目錄的,jni_md.h不需要另外創(chuàng)建一個win32目錄來存放。完成后發(fā)現(xiàn)com_example_jni_JNIObject.h的報錯消失了。
我們右鍵點擊項目,選擇New->C/C++ Source File,然后創(chuàng)建一個源碼文件,type選擇.c,如果你習(xí)慣使用C++就選.cpp
點擊OK完成,下面是具體的實現(xiàn)
#include "com_example_jni_JNIObject.h"
JNIEXPORT jint JNICALL Java_com_example_jni_JNIObject_multi
(JNIEnv *env, jobject o, jint param1, jint param2) {
return param1 * param2;
}
JNIEXPORT jint JNICALL Java_com_example_jni_JNIObject_div
(JNIEnv *env, jobject o, jint param1, jint param2) {
return param1 / param2;
}
解釋一下上面的源文件,#include是把后面的com_example_jni_JNIObject.h頭文件包含進來,和java的import作用類似,下面的兩個方法就是在這個頭文件中聲明的函數(shù),c語言在聲明函數(shù)時可以忽略參數(shù)名,只寫參數(shù)類型,但是現(xiàn)在我們是在實現(xiàn)函數(shù),所以必須加上參數(shù)名。如果是學(xué)習(xí)過c語言的很容易理解,沒學(xué)過的了解一下就好。
編譯動態(tài)鏈接庫
到此,我們的代碼就完成了,點擊菜單欄的Build->Build Project成功后在在左側(cè)的項目結(jié)構(gòu)里生成了一些文件,其中cmake-build-debug目錄下的libjni.dll就是我們需要的動態(tài)鏈接庫了,如果是linux系統(tǒng),生成的是.so格式的文件。
(3)在java中調(diào)用native方法
回到j(luò)ava目錄,我們在項目的根目錄下創(chuàng)建一個jni目錄,把我們的dll文件復(fù)制進去,復(fù)制好之后會自動打開,發(fā)現(xiàn)是亂碼,因為dll文件是二進制格式的,我們直接關(guān)掉。
在JNIObject類中添加一個靜態(tài)代碼塊,用來加載我們的動態(tài)鏈接庫,完成后的JNIObject類如下:
public class JNIObject {
static {
System.loadLibrary("libjni");
}
private String name;
public JNIObject(String name) {
this.name = name;
}
...
}
注意loadLibrary方法不用寫dll后綴名。
我們新建一個測試類Main,代碼如下:
package com.example.main;
import com.example.jni.JNIObject;
public class Main {
public static void main(String[] args) {
JNIObject jniObject = new JNIObject("jni");
System.out.println(jniObject.getName()); // 調(diào)用java方法
System.out.println(jniObject.add(1, 2)); // 調(diào)用java方法
System.out.println(jniObject.sub(1, 2)); // 調(diào)用java方法
System.out.println(jniObject.multi(2, 3)); // 調(diào)用native方法
System.out.println(jniObject.div(6, 2)); // 調(diào)用native方法
}
}
我們先運行一個Main類的main方法,發(fā)現(xiàn)報錯了:
Exception in thread "main" java.lang.UnsatisfiedLinkError: no libjni in java.library.path
at java.lang.ClassLoader.loadLibrary(ClassLoader.java:1860)
at java.lang.Runtime.loadLibrary0(Runtime.java:870)
at java.lang.System.loadLibrary(System.java:1122)
at com.example.jni.JNIObject.(JNIObject.java:6)
at com.example.main.Main.main(Main.java:8)
這是因為我們還沒有指定jni庫的加載路徑,導(dǎo)致loadLibrary方法無法找到我們的dll庫。點開運行按鈕下拉菜單的Edit Configurations,我們給Main類加一個啟動參數(shù)-Djava.library.path,這個參數(shù)就是異常信息出現(xiàn)的參數(shù),指定值為jni目錄
重新運行main方法,可以看到已經(jīng)可以正常執(zhí)行native方法了。
總結(jié)
總結(jié)一下jni的調(diào)用過程,先定義好native方法,然后通過javah生成頭文件,然后用C或C++實現(xiàn)函數(shù),編譯成動態(tài)鏈接庫,把動態(tài)鏈接庫加入到j(luò)ava項目當(dāng)中,通過System.loadLibrary(String)方法加載,最后就可以調(diào)用了。
總結(jié)
以上是生活随笔為你收集整理的clion 引用dll_用CLion实现本地方法并给java调用的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: bool类型0和1真假_MySQL整理5
- 下一篇: linux bluez语音传输,Linu