版權聲明:著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。
我的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;
引用類型數組
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.h
和jni_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);
}