JNI淺嘗

什么是JNI

百度百科:https://baike.baidu.com/item/JNI/9412164
當(dāng)然,里面也有關(guān)于JNI的實(shí)現(xiàn),懂的看官就不必往下看啦,大致上差不多。

開發(fā)環(huán)境

  • Mac電腦
  • IntelliJ IDEA(寫java代碼)
  • Clion(寫C/C++代碼)
  • 配置好java運(yùn)行環(huán)境

開始寫代碼

第一步,在IDEA里創(chuàng)建一個(gè)java工程,創(chuàng)建一個(gè)java類

Register.java

public class Register {
    public native void staticReg();

    public static void main(String[] args) {
        Register register = new Register();
        register.staticReg();
    }
}

說明:public native void staticReg();native修飾的方法就是C方法,相當(dāng)于這里定義一個(gè)接口,讓C代碼去實(shí)現(xiàn)。
為了讓C代碼可以知道這個(gè)接口,我們需要進(jìn)行以下兩步操作:

1. 使用javac Register.java命令,把java類編譯成.class文件

注意:需要在Register.java文件的目錄下執(zhí)行該命令。
生成的.class文件內(nèi)容如下:
Register.class

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

public class Register {
    public Register() {
    }

    public native void staticReg();

    public static void main(String[] var0) {
        Register var1 = new Register();
        var1.staticReg();
    }

看起來(lái)跟java代碼差不多

2. 使用javah Register命令,根據(jù)剛才生成的.class文件,生成一個(gè).h頭文件

注意:javah 命令后面的Register是不帶后綴的
Register.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class Register */

#ifndef _Included_Register
#define _Included_Register
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     Register
 * Method:    staticReg
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT void JNICALL Java_Register_staticReg
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

可以看到,我們從最開始的java文件,生成了最終的.h頭文件。了解C語(yǔ)言的同學(xué)大概已經(jīng)猜到了,這個(gè)頭文件里定義了一個(gè)java文件里public native void staticReg()對(duì)應(yīng)的方法Java_Register_staticReg (JNIEnv *, jobject)

既然已經(jīng)有了C的頭文件,我們是不是在我們的C的library項(xiàng)目里引用這個(gè)頭文件,并實(shí)現(xiàn)其中的方法,就可以生成一個(gè)動(dòng)態(tài)庫(kù)了呢?
說干就干 ~

第二步,在Clion里創(chuàng)建一個(gè)C++ Library工程register

創(chuàng)建library工程.png

創(chuàng)建完成之后的目錄結(jié)構(gòu)是這樣子的

C++Library目錄結(jié)構(gòu).png

可以看到,創(chuàng)建的時(shí)候會(huì)默認(rèn)生成一個(gè)library.h和一個(gè)library.cpp文件。這里我們是要自己引入.h頭文件、再實(shí)現(xiàn)這個(gè)頭文件的方法,所以可以刪掉這個(gè)這兩個(gè)文件。

現(xiàn)在我們找到上面生成的.h頭文件,復(fù)制粘貼到libaray目錄下,
然后修改CMakeLists.txt文件

配置.h文件.png

切換到Register.h文件,發(fā)現(xiàn)有報(bào)錯(cuò)


找不到依賴的頭文件.png

那么這個(gè)依賴在哪里呢?我們需要找到依賴的.h文件,復(fù)制到libaray項(xiàng)目下面。
這個(gè)jni.h在jdk里面/Library/Java/JavaVirtualMachines/jdk1.8.0_162.jdk/Contents/Home/include/jni.h
復(fù)制完之后,看到還報(bào)錯(cuò),需要把尖括號(hào)改成雙引號(hào)

.h引用方式不對(duì),需要改成雙引號(hào)引用.png

未識(shí)別JNIEXPORT.png

這里需要再引入一個(gè)頭文件jni_md.hjni.h里引用了這個(gè)),目錄/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/JavaVM.framework/Versions/A/Headers/jni_md.h(看官可自行搜索)

jni_md.h,jni.h都復(fù)制粘貼之后,可以看到Register.h文件就已經(jīng)不報(bào)錯(cuò)了。

然后創(chuàng)建一個(gè)Register.cpp文件,并引用Register.h頭文件,實(shí)現(xiàn)里面的方法
Register.cpp

//
// Created by wh on 2019/9/27.
//
#include "Register.h" //引用頭文件

/*
 * Class:     Register
 * Method:    staticReg
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT void JNICALL Java_Register_staticReg
        (JNIEnv *env, jobject obj) {
    printf("this is my first jni program"); //實(shí)現(xiàn)頭文件中定義的方法,打印一個(gè)字符串
}

到這里,代碼基本上寫完了。

現(xiàn)在我們把Libaray工程build一下,可以看到,生成了一個(gè)libregister.dylib文件:

動(dòng)態(tài)庫(kù)文件.png

這個(gè)文件就是生成的動(dòng)態(tài)庫(kù)了。

引用動(dòng)態(tài)庫(kù)

我們復(fù)制libregister.dylib的絕對(duì)路徑,切回到j(luò)ava工程,修改Register.java

public class Register {
    //靜態(tài)加載庫(kù)文件
    static {
        System.load("/Users/wh/CLionProjects/register/cmake-build-debug/libregister.dylib");
    }

    public native void staticReg();

    public static void main(String[] args) {
        Register register = new Register();
        register.staticReg();
    }
}

運(yùn)行一下main函數(shù):


大功告成.png

問題

  1. Android開發(fā)平時(shí)用到的動(dòng)態(tài)庫(kù)不是.so結(jié)尾的嗎,.dylib是什么鬼?

  2. 什么是靜態(tài)注冊(cè)?什么是動(dòng)態(tài)注冊(cè)?

  3. C/C++中如何調(diào)用java代碼?

  4. jni在編寫和使用的過程中,有什么需要特別注意的地方嗎?

待續(xù)...

  1. 本人是個(gè)初學(xué)者,本篇文章記錄了自已編寫代碼的過程以及思路,可能有些模棱兩可的情況,歡迎批評(píng)指正。
  2. 上面只說的都只是JNI的靜態(tài)注冊(cè),計(jì)劃后續(xù)更新動(dòng)態(tài)注冊(cè)代碼編寫過程。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容