版權(quán)聲明:著作權(quán)歸作者所有诱担。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán)窑滞,非商業(yè)轉(zhuǎn)載請注明出處琼牧。
我的CSDN地址:http://blog.csdn.net/urrjdg
本文CSDN地址:http://blog.csdn.net/urrjdg/article/details/78153147
CSDN 和 簡書 同步更新
看目錄去CSDN
1.介紹 - JNI/NDK/靜態(tài)庫/動態(tài)庫
1.JNI
java native interface
Java中定義的一種用于連接Java和C/C++接口的一種實現(xiàn)方式
使用環(huán)境:
java api 不能滿足我們程序的需要的時候恢筝。
算法計算,圖像渲染 效率要求非常高障陶,
當(dāng)需要訪問一些已有的本地庫
2.NDK
Native Development Kit
工具的集合滋恬。幫助開放者快速開放C/C++ 動態(tài)庫的工具。
是Google在Android開發(fā)中提供的一套用于快速創(chuàng)建native工程的一個工具抱究。使用這個工具可以很方便的編寫恢氯,調(diào)試JNI的代碼。
3.靜態(tài)庫和動態(tài)庫
都是函數(shù)庫鼓寺。
靜態(tài)庫:.a (靜態(tài)庫在程序編譯的時候就會直接連接到目標(biāo)代碼里面勋拟,所以在運(yùn)行的時候就不需要了 )
動態(tài)庫: .dll/.so (動態(tài)庫在編譯的時候不會自動連接到目標(biāo)代碼里面,就是說 這個動態(tài)庫是獨(dú)立吧妈候,不會隨著程序的編譯直接鏈接進(jìn)去敢靡,而是在程序運(yùn)行的時候動態(tài)加載的,例如一下代碼)
// 只有在運(yùn)行工程的時候才會去走 這個static的代碼塊苦银,這就是動態(tài)的過程
static{
System.loadLibrary("JNI_Native");
}
好處:功能獨(dú)立啸胧,作為方案提供商不需要提供源碼(保密)
4.JNIEnv 是什么?
C:
JNIEnv 結(jié)構(gòu)體指針的別名
env 是二級指針
C++
JNIEnv 是結(jié)構(gòu)體的別名
env 是一級指針
每個native 函數(shù)幔虏,都至少有兩個參數(shù)(JNIEnv * , jclass/jobject)
jclass: native 靜態(tài)方法
jobject: native 非靜態(tài)方法
5. 數(shù)據(jù)類型
Jni基本數(shù)據(jù)類型
引用類型
String jstring在·
Object jobject
基本數(shù)據(jù)類型數(shù)組:
//type[] jTypeArray;
byte[] jByteArray;
引用類型數(shù)組
6. 屬性簽名
例如:Java方法:
long f(int n,String s,int[] arr);
具有以下類型簽名:
(ILjava/lang/String;[I)J
2. 使用jni 進(jìn)行 java 調(diào)用 C 的 靜態(tài) 和非靜態(tài)navtive方法
1. 新建一個 Java工程
public class JniTest01 {
// 靜態(tài)方法
public native static String getStringFromC();
// 非靜態(tài)方法
public native String getStringFromC2();
public static void main(String[] args) {
System.out.println("Test01");
}
}
2. 使用 javah 命令
使用 cmd 跳轉(zhuǎn)到 JniTest01這個類 的當(dāng)前 目錄下 纺念,直接敲 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. 復(fù)制.h 頭文件到cpp 工程 (Vistual Studio)
復(fù)制 .h 文件的 時候要先拷貝到 工程所在的頭文件夾下面想括, 然后在 visual studio的工程那邊 : 頭文件右鍵 添加--》 現(xiàn)有項
4. 復(fù)制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. 實現(xiàn).h 頭文件中的聲明函數(shù)
這是個C文件 不是Cpp陷谱,先用C進(jìn)行演示,后面的系列會用到C++
#include "JniTest01.h"
#include "stdafx.h"
/*
* Class: JniTest01
* Method: getStringFromC
* Signature: ()Ljava/lang/String;
*/
// 靜態(tài)方法 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;
*/
// 非靜態(tài)方法 jobject
JNIEXPORT jstring JNICALL Java_JniTest01_getStringFromC2
(JNIEnv * env, jobject jclz){
return(*env)->NewStringUTF(env, "String frome C 02,ZekingLee");
}
6. 生成一個dll 動態(tài)庫
右鍵 visual studio 的項目名 --》 屬性 --》 配置屬性 --》 常規(guī) --》 配置類型 --》 動態(tài)庫(.dll)
平臺使用x64
如果出現(xiàn) : 錯誤 1 error C1083: 無法打開預(yù)編譯頭文件: “x64\Debug\ConsoleApplication1.pch”: No such file or directory C:\Users\Zeking\Desktop\JNICode\Jni01\ConsoleApplication1\ConsoleApplication1\JniTest01.c 2 1 ConsoleApplication1
右鍵工程名字-->屬性-->配置屬性-->C/C++ -->預(yù)編譯頭--> 選擇不適用預(yù)編譯頭
7. 在java中加載動態(tài)庫
將dll
文件 復(fù)制到 java工程里面
8. 觸發(fā)native函數(shù)
public class JniTest01 {
// 靜態(tài)方法
public native static String getStringFromC();
// 非靜態(tài)方法
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 進(jìn)行C 調(diào)用 java 的 靜態(tài) 和非靜態(tài)變量
遇到這個錯誤的解決方法:
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++-->預(yù)處理器-->預(yù)處理定義 添加 _CRT_SECURE_NO_WARNINGS
public class JniTest02 {
// 非靜態(tài)變量
public String key = "Key";
// 靜態(tài)變量
public static int count = 99;
// 調(diào)用非靜態(tài)變量
public native String accessField();
// 調(diào)用靜態(tài)變量
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;
*
*/
// 訪問非靜態(tài)域
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 對應(yīng)的值
// 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
*/
// 訪問靜態(tài)域
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);
}