溫馨提示
寫博客是為了記錄在開發(fā)過程中所涉及到的技術(shù)以及遇到的問題的解決,如果該博客對您有所幫助,希望可以點(diǎn)個(gè)關(guān)注/喜歡;如果您對文章中的內(nèi)容有什么不同的見解,歡迎留言進(jìn)行討論。謝謝!
JNI 開發(fā)流程
一、C 語言執(zhí)行的流程
- 編輯:編寫代碼的過程。
- 預(yù)編譯(預(yù)處理):為編譯做準(zhǔn)備工作,完成代碼文本的替換工作。
- 編譯:形成目標(biāo)代碼(.obj)。
- 連接:將目標(biāo)代碼與C 函數(shù)庫連接合并,形成最終的可執(zhí)行文件。
- 執(zhí)行:執(zhí)行可執(zhí)行文件。
二、頭文件
1、頭文件的作用
頭文件告訴編譯器有這樣一個(gè)函數(shù),連接器負(fù)責(zé)找到這個(gè)函數(shù)的實(shí)現(xiàn)
2、自定義頭文件
*注:開發(fā)工具為 Visual Studio 2017
1、創(chuàng)建 .h 文件,對相應(yīng)方法進(jìn)行聲明。
例如:創(chuàng)建 math.h
#ifndef _MATH_H //如果沒有定義 _MATH_H 標(biāo)識
#define _MATH_H //定義 _MATH_H 標(biāo)識
int add(int, int, int);
#endif
或
//該頭文件只被包含一次,讓編譯器自己處理好循環(huán)包含問題
#pragma once
int add(int, int, int);
2、在 .h 文件同級目錄下創(chuàng)建對應(yīng)的 .c 文件,對 .h 文件中聲明的方法進(jìn)行實(shí)現(xiàn)。
例如:創(chuàng)建 head.c
#define _CRT_SECURE_NO_WARNINGS
#include "math.h"
#include <stdio.h>
int add(int a, int b, int c){
int result = 0;
result = a + b + c;
return result;
}
3、創(chuàng)建一個(gè)C文件,進(jìn)行驗(yàn)證頭文件是否編寫成功。
例如:創(chuàng)建 test.c
#include<stdio.h>
#include "math.h"
void main(){
int a = 3, b = 4, c = 5, result = 0;
result = add(a, b, c);
printf("The result is %d!\n", result);
system("pause");
}
三、define 指令
1、define 指令的作用
- define 指令用來定義標(biāo)識;
如: #ifdef __cplusplus 標(biāo)識支持C++語法;防止文件重復(fù)引入 - define 指令用來定義常數(shù);如:#define MAX 100
- define 指令用來定義“宏函數(shù)”。
如:
void jni_read(){
printf("read\n");
}
void jni_write(){
printf("write\n");
}
//宏函數(shù)
#define jin(NAME) jni_##NAME();
void main(){
jni(read);
jni(write);
getchar();
}
日志輸出示例:
//__VA_ARGS__ 可變參數(shù)
#define LOG(FOTMAT,...) printf(##FOTMAT,__VA_ARGS__);printf("\n");
void main(){
LOG("%s: %d","size",99);
getchar();
}
四、JNI (Java Native Interface)
1、定義
**Java 調(diào)用C/C++或者C/C++調(diào)用 Java 的一套 API **
2、Java調(diào)用C/C++項(xiàng)目開發(fā)步驟(Windows系統(tǒng)下)
- 編寫native方法
package com.example.jni;
public class JNITest {
public native static String getStringFromC();
public static void main(String[] args){
}
}
- javah命令,生成.h文件
javah com.example.jni.JNITest
//生成 com_example_jni_JNITest.h 文件
- 復(fù)制.h頭文件到CPP工程中
- 復(fù)制jni.h和jni_md.h文件到CPP工程中
- 實(shí)現(xiàn).h頭文件中聲明的函數(shù);C函數(shù)名稱:Java_完整類名_函數(shù)名
//JNITest.c
#include "com_example_jni_JNITest.h"
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_getStringFromC
(JNIEnv *jEnv, jclass jcls) {
//簡單實(shí)現(xiàn),將C的字符傳轉(zhuǎn)成Java的字符串
return (*jEnv)->NewStringUTF(jEnv, "C String");
}
- 生成動態(tài)庫.dll文件(Windows環(huán)境下默認(rèn)dll,Linux環(huán)境下默認(rèn)為so)
- 配置D:\dll 目錄到環(huán)境變量,并將剛剛生成的 .dll 文件復(fù)制到D:\dll 目錄下;或者復(fù)制到項(xiàng)目根目錄下;
- 重啟Eclipse,使用IDEA的需要在項(xiàng)目運(yùn)行配置中的 VM options 中增加配置:
// VM options:
-Djava.library.path=D:\dll
五、JNIEnv
1、JNIEnv 是什么
- 在C語言中JNIEnv是一個(gè)結(jié)構(gòu)體指針,代表Java運(yùn)行環(huán)境,主要是調(diào)用Java中的代碼,在上面JNITest.c中實(shí)現(xiàn)函數(shù)聲明的時(shí)候,jEnv 是一個(gè)二級指針
//JNITest.c
#include "com_example_jni_JNITest.h"
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_getStringFromC
(JNIEnv *jEnv, jclass jcls) {
//簡單實(shí)現(xiàn),將C的字符傳轉(zhuǎn)成Java的字符串
return (*jEnv)->NewStringUTF(jEnv, "C String");
}
- 在C++中JNIEnv是一個(gè)結(jié)構(gòu)體的別名,代表Java運(yùn)行環(huán)境,主要是調(diào)用Java中的代碼,jEnv 是一個(gè)結(jié)構(gòu)體的一級指針
//JNITest.cpp
#include "com_example_jni_JNITest.h"
JNIEXPORT jstring JNICALL Java_com_example_jni_JNITest_getStringFromC
(JNIEnv *jEnv, jclass jcls) {
//簡單實(shí)現(xiàn),將C的字符傳轉(zhuǎn)成Java的字符串
return jEnv->NewStringUTF("C String");
}
模擬C 的實(shí)現(xiàn)
//JNIEnv 是結(jié)構(gòu)體指針的別名
typedef struct JNINativeInferface_* JNIEnv;
//結(jié)構(gòu)體
struct JNINativeInferface_{
char* (*NewStringUTF)(JNIEnv*,char*);
};
//函數(shù)實(shí)現(xiàn)
char* NewStringUTF(JNIEnv* env,char* str){
return str;
}
void main(){
//實(shí)例化結(jié)構(gòu)體
struct JNINativeInferface_ struct_env;
struct_env.NewStringUTF = NewStringUTF;
//結(jié)構(gòu)體指針
JNIEnv e = &struct_env;
//結(jié)構(gòu)體的二級指針
JNIEnv *env = &e;
//通過二級指針調(diào)用函數(shù)
char* str = (*env)->NewStringUTF(env,"Hello");
printf("str = %s\n",str);
getchar();
}
2、JNIEnv 調(diào)用函數(shù)時(shí)C和C++的區(qū)別
- C 中需要傳入 JNIEnv ,因?yàn)楹瘮?shù)執(zhí)行過程中需要 JNIEnv
- C++ 中不需要傳入 JNIEnv ,是因?yàn)镃++中有 this,相當(dāng)與JNIEnv
- C++只是針對C的那一套進(jìn)行分裝,給一個(gè)變量賦值為指針,這個(gè)變量是二級指針
六、jclass
每個(gè)native函數(shù)(C中的函數(shù)),都至少有兩個(gè)參數(shù)(JNIEnv* jclass或者jobject)。
- 當(dāng)native方法為靜態(tài)方法時(shí):jclass代表native 方法所屬類的class對象(JNITest.class);
- 當(dāng) native 方法為非靜態(tài)方法時(shí):jobject 代表 native 方法所屬的對象。