(一)JNI 開發(fā)流程

溫馨提示

寫博客是為了記錄在開發(fā)過程中所涉及到的技術(shù)以及遇到的問題的解決,如果該博客對您有所幫助,希望可以點(diǎn)個(gè)關(guān)注/喜歡;如果您對文章中的內(nèi)容有什么不同的見解,歡迎留言進(jìn)行討論。謝謝!

JNI 開發(fā)流程

一、C 語言執(zhí)行的流程

  1. 編輯:編寫代碼的過程。
  2. 預(yù)編譯(預(yù)處理):為編譯做準(zhǔn)備工作,完成代碼文本的替換工作。
  3. 編譯:形成目標(biāo)代碼(.obj)。
  4. 連接:將目標(biāo)代碼與C 函數(shù)庫連接合并,形成最終的可執(zhí)行文件。
  5. 執(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 指令的作用

  1. define 指令用來定義標(biāo)識;
    如: #ifdef __cplusplus 標(biāo)識支持C++語法;防止文件重復(fù)引入
  2. define 指令用來定義常數(shù);如:#define MAX 100
  3. 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 方法所屬的對象。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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