JNI 學習筆記(一)-- jni函數調用流程,JNI理解和基本數據類型

版權聲明:著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
我的CSDN地址:http://blog.csdn.net/urrjdg
本文CSDN地址:http://blog.csdn.net/urrjdg/article/details/78153147
CSDN 和 簡書 同步更新

看目錄去CSDN

1.介紹 - JNI/NDK/靜態庫/動態庫

1.JNI

java native interface
Java中定義的一種用于連接Java和C/C++接口的一種實現方式
使用環境:
java api 不能滿足我們程序的需要的時候。
算法計算,圖像渲染 效率要求非常高,
當需要訪問一些已有的本地庫

2.NDK

Native Development Kit
工具的集合。幫助開放者快速開放C/C++ 動態庫的工具。
是Google在Android開發中提供的一套用于快速創建native工程的一個工具。使用這個工具可以很方便的編寫,調試JNI的代碼。

3.靜態庫和動態庫

都是函數庫。
靜態庫:.a (靜態庫在程序編譯的時候就會直接連接到目標代碼里面,所以在運行的時候就不需要了 )
動態庫: .dll/.so (動態庫在編譯的時候不會自動連接到目標代碼里面,就是說 這個動態庫是獨立吧,不會隨著程序的編譯直接鏈接進去,而是在程序運行的時候動態加載的,例如一下代碼)

// 只有在運行工程的時候才會去走 這個static的代碼塊,這就是動態的過程     
static{
    System.loadLibrary("JNI_Native");
}

好處:功能獨立,作為方案提供商不需要提供源碼(保密)

4.JNIEnv 是什么?

C:
JNIEnv 結構體指針的別名
env 是二級指針

C++
JNIEnv 是結構體的別名
env 是一級指針

每個native 函數,都至少有兩個參數(JNIEnv * , jclass/jobject)
jclass: native 靜態方法
jobject: native 非靜態方法

5. 數據類型

Jni基本數據類型

基本數據類型

引用類型

String jstring在·
Object jobject

引用類型

基本數據類型數組:

//type[] jTypeArray;
byte[] jByteArray;

引用類型數組

Object jobjectArray;

6. 屬性簽名

屬性簽名

例如:Java方法:

long f(int n,String s,int[] arr);

具有以下類型簽名:

(ILjava/lang/String;[I)J

2. 使用jni 進行 java 調用 C 的 靜態 和非靜態navtive方法

1. 新建一個 Java工程


public class JniTest01 {

    // 靜態方法
    public native static String getStringFromC();
    // 非靜態方法
    public native String getStringFromC2();
    
    public static void main(String[] args) {
        System.out.println("Test01");
    }
    
}

2. 使用 javah 命令

使用 cmd 跳轉到 JniTest01這個類 的當前 目錄下 ,直接敲 javah ,生成 JniTest01.h 文件( 或者自己手寫:java_類的全名_方法名)

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

#ifndef _Included_JniTest01
#define _Included_JniTest01
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     JniTest01
 * Method:    getStringFromC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC
  (JNIEnv *, jclass);

/*
 * Class:     JniTest01
 * Method:    getStringFromC2
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC2
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

3. 復制.h 頭文件到cpp 工程 (Vistual Studio)

復制 .h 文件的 時候要先拷貝到 工程所在的頭文件夾下面, 然后在 visual studio的工程那邊 : 頭文件右鍵 添加--》 現有項

4. 復制jni.h 和jni_md.h

同上

jni.hjni_md.h

C:\develop\Java\jdk1.8.0_111\include
C:\develop\Java\jdk1.8.0_111\include\win32

記得修改 JniMain.h#include <jni.h>#include "jni.h"

5. 實現.h 頭文件中的聲明函數

這是個C文件 不是Cpp,先用C進行演示,后面的系列會用到C++

#include "JniTest01.h"
#include "stdafx.h"
/*
* Class:     JniTest01
* Method:    getStringFromC
* Signature: ()Ljava/lang/String;
*/
// 靜態方法 jclass
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC
(JNIEnv * env, jclass jclz){
    return(*env)->NewStringUTF(env,"String frome C 01,ZekingLee");
}

/*
* Class:     JniTest01
* Method:    getStringFromC2
* Signature: ()Ljava/lang/String;
*/
// 非靜態方法 jobject
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC2
(JNIEnv * env, jobject jclz){
    return(*env)->NewStringUTF(env, "String frome C 02,ZekingLee");
}

6. 生成一個dll 動態庫

右鍵 visual studio 的項目名 --》 屬性 --》 配置屬性 --》 常規 --》 配置類型 --》 動態庫(.dll)

平臺使用x64

如果出現 : 錯誤 1 error C1083: 無法打開預編譯頭文件: “x64\Debug\ConsoleApplication1.pch”: No such file or directory C:\Users\Zeking\Desktop\JNICode\Jni01\ConsoleApplication1\ConsoleApplication1\JniTest01.c 2 1 ConsoleApplication1

右鍵工程名字-->屬性-->配置屬性-->C/C++ -->預編譯頭--> 選擇不適用預編譯頭

7. 在java中加載動態庫

dll文件 復制到 java工程里面

8. 觸發native函數


public class JniTest01 {

    // 靜態方法
    public native static String getStringFromC();
    // 非靜態方法
    public native String getStringFromC2();
    
    public static void main(String[] args) {
        System.out.println(getStringFromC());  // 輸出  String frome C 01,ZekingLee
        
        JniTest01 jinTest01 = new JniTest01();
        System.out.println(jinTest01.getStringFromC2()); // 輸出  String frome C 02,ZekingLee
         
    }
    
    static{
        System.loadLibrary("ConsoleApplication1");
    }
    
}

3. 使用jni 進行C 調用 java 的 靜態 和非靜態變量

遇到這個錯誤的解決方法:

error C4996: 'strcat': This function or variable may be unsafe. Consider using strcat_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details. C:\Users\Zeking\Desktop\JNICode\Jni01\ConsoleApplication1\ConsoleApplication1\JniTest01.c 51 1 ConsoleApplication1

項目名右鍵--> 屬性-->C/C++-->預處理器-->預處理定義 添加 _CRT_SECURE_NO_WARNINGS

public class JniTest02 {
    // 非靜態變量
    public String key = "Key";
    // 靜態變量
    public static int count = 99;
    // 調用非靜態變量
    public native String accessField();
    // 調用靜態變量
    public native void accessStaticField();
    
    static{
        System.loadLibrary("ConsoleApplication1");
    }
    
    public static void main(String[] args) {
        

        JniTest02 jniTest02 = new JniTest02();
        System.out.println(jniTest02.key);  // Key
        jniTest02.accessField();
        System.out.println(jniTest02.key);  // ZekingKey
        
        System.out.println(count);          // 99
        jniTest02.accessStaticField();
        System.out.println(count);          // 100
        
    }

}

#include "JniTest02.h"
#include "stdafx.h"
#include <string.h>
#include <stdio.h>

/*
* Class:     JniTest02
* Method:    accessField
* Signature: ()Ljava/lang/String;
* 
*/
// 訪問非靜態域 
JNIEXPORT jstring JNICALL Java_JniTest02_accessField
(JNIEnv * env, jobject jobj){
    // jclass
    jclass jclz = (*env)->GetObjectClass(env, jobj);

    // fieldId 屬性名稱,屬性簽名
    jfieldID fid = (*env)->GetFieldID(env, jclz, "key", "Ljava/lang/String;");
    // key -> dongNao key

    // 得到key 對應的值
    // GetXXXField
    jstring jstr = (*env)->GetObjectField(env, jobj, fid);

    // jni -> c
    char * c_str = (*env)->GetStringUTFChars(env, jstr, NULL);

    char text[30] = "Zeking";
    // 生成新的字符串 ZekingKey
    strcat(text, c_str);

    // C -> jni
    jstring new_str = (*env)->NewStringUTF(env, text);

    //setXXXField
    (*env)->SetObjectField(env, jobj, fid, new_str);

    (*env)->ReleaseStringChars(env, new_str, c_str);

    return new_str;
}

/*
* Class:     JniTest02
* Method:    accessStaticField
* Signature: ()V
*/
// 訪問靜態域
JNIEXPORT void JNICALL Java_JniTest02_accessStaticField
(JNIEnv * env, jobject jobj){

    jclass jclz = (*env)->GetObjectClass(env, jobj);

    jfieldID fid = (*env)->GetStaticFieldID(env, jclz, "count", "I");

    if (fid == NULL){
        printf("fid is Null");
    }

    jint count = (*env)->GetStaticIntField(env, jclz, fid);
    count++;

    (*env)->SetStaticIntField(env, jclz, fid, count);



}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容