JNI开发系列②.h头文件分析

接续上篇JNI开发系列①JNI概念及开发流程

前情提要

JNI技术 , 是java世界与C/C++世界的通信基础 , java语言可以通过native方法去调用C/C++的函数 , 也可以通过C/C++来调用java的字段与方法 。 在上篇中 , 我们了解了JNI开发的基本流程 , 接下来我们来分析分析C语言代码以及头文件 。

.h头文件分析

头文件生成命令 : javah com.zeno.jni.HelloJni

public static native String getStringFromC() ;

上述代码 通过javah命令 , 则会生成如下头文件中的函数:

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

#ifndef _Included_com_zeno_jni_HelloJni
#define _Included_com_zeno_jni_HelloJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_zeno_jni_HelloJni
 * Method:    getStringFormC
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJni_getStringFromC
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

通过上述代码, 我们可以看出getStringFromC()方法 , 生成的函数是Java_com_zeno_jni_HelloJni_getStringFromC (JNIEnv *, jclass)函数 。其实 ,我们不用javah命令 , 也能写出头文件 , 除了#endif,#ifdef __cplusplus中间是变化的 , 其他的都不变 , javah通过native方法生成的函数 , 命名都是有规律 。

函数名称规则:Java_完整类名_方法名  , 包名的.号 , 以`_`表示

其中jstring是返回的java的String类型 , jstring类型是jni里面定义的类型 , 标准C里面是没有的 。那么 , jstring是什么类型呢 ?
使用VS的转到定义功能 , 我们可以看到 , jstring在jni.h的定义 , jstring是jobject的别名 , jobject是一个_jobject结构体的指针 。

typedef jobject jstring;
typedef struct _jobject *jobject;

因为我们getStringFromC()方法返回的是一个String类型 , 所以C函数的返回值是jstring类型 。

/*
* Class:     com_zeno_jni_HelloJni
* Method:    getStringFormC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJni_getStringFromC
(JNIEnv *Env, jclass jclazz) {

    return (*Env)->NewStringUTF(Env, "Jni C String");
}

在C语言系列的最后一篇 , 我们分析了jni.h的头文件 , 了解了JNIEnv结构体指针 , 大致知道里面都有些什么函数 , NewStringUTF(Env, "Jni C String")这个函数 , 就是将C语言中的字符指针转换成java的String类型的字符串。

JNI数据类型对应java的标准数据类型

Java Type Native Type Description
boolean jboolean unsigned 8 bits
byte jbyte signed 8 bits
char jchar unsigned 16 bits
short jshort signed 16 bits
int jint signed 32 bits
long jlong signed 64 bits
float jfloat 32 bits
double jdouble 64 bits
void void not applicable

JNI数据类型对应java的引用数据类型

struct _jobject;

typedef struct _jobject *jobject;
typedef jobject jclass;
typedef jobject jthrowable;
typedef jobject jstring;
typedef jobject jarray;
typedef jarray jbooleanArray;
typedef jarray jbyteArray;
typedef jarray jcharArray;
typedef jarray jshortArray;
typedef jarray jintArray;
typedef jarray jlongArray;
typedef jarray jfloatArray;
typedef jarray jdoubleArray;
typedef jarray jobjectArray;

在jni源码中 , 我们可以看到如上定义 , 可以发现 , 所有的引用类型都是_jobject结构体指针类型。

JNIEnv分析

我们知道 ,JNIEnv是JNINativeInterface_结构体的指针别名 , 在JNINativeInterface_结构体中 , 定义很多操作函数 。例如:

jstring (JNICALL *NewStringUTF) (JNIEnv *env, const char *utf);
jsize (JNICALL *GetStringUTFLength) (JNIEnv *env, jstring str);
const char* (JNICALL *GetStringUTFChars)(JNIEnv *env, jstring str, jboolean *isCopy);
 void (JNICALL *ReleaseStringUTFChars)(JNIEnv *env, jstring str, const char* chars);

由上述函数可以看出,每个函数都需要一个JNIEnv指针,但是为什么需要呢 ?

有两点:
第一:函数需要 , 在函数中仍然需要JNINativeInterface_结构体中的函数做处理

第二:区别对待C和C++
我们知道 , jni是支持C/C++的,在jni.h头文件中 , 那么C++是怎么表示JNIEnv的呢 ?源码如下:

 struct JNIEnv_ {
    const struct JNINativeInterface_ *functions;
#ifdef __cplusplus

    jint GetVersion() {
        return functions->GetVersion(this);
    }
    jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
                       jsize len) {
        return functions->DefineClass(this, name, loader, buf, len);
    }
    jclass FindClass(const char *name) {
        return functions->FindClass(this, name);
    }
    jmethodID FromReflectedMethod(jobject method) {
        return functions->FromReflectedMethod(this,method);
    }

在C++环境下 ,还是使用的NINativeInterface_结构体指针调用函数, 使用NewStringUTF函数时, 则不需要传入Env这个二级指针 ,因为C++是面向对象的语言 , 传入了this , 当前环境的指针

jstring NewStringUTF(const char *utf) {
        return functions->NewStringUTF(this,utf);
    }

示例:

/*
* Class:     com_zeno_jni_HelloJni
* Method:    getStringFormC
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_zeno_jni_HelloJni_getStringFromCPP
(JNIEnv * env, jclass jclazz) {

    return env->NewStringUTF("From C++ String");
}

在C++环境中 , JNIEnv就成了一级指针了 , 为什么会是这样呢 ?我们在源码中找到这样一段代码:

/*
 * JNI Native Method Interface.
 */

struct JNINativeInterface_;

struct JNIEnv_;

#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv;
#endif

由上可知 , 在C和C++两个环境中 , 使用了两个不同的JNIEnv , 一个是JNIEnv二级指针 , 一个是JNIEnv一级指针 。

模拟C语言中的JNIEnv写法


#include <stdio.h>
#include <stdlib.h>

// 定义一个JNIEnv , 是JavaNativeInterface结构体指针的别名
typedef struct JavaNativeInterface* JNIEnv;

// 模拟java本地化接口结构体
struct JavaNativeInterface {
    void(*hadlefunc)(JNIEnv*);
    char*(*NewStringUTF)(JNIEnv*, char*);
};

// 模拟 , 在调用NewStringUTF函数的时候 , 需要处理一些事情
void handleFunction(JNIEnv* env) {
    printf("正在处理...\n");
}

// 最终调用的函数实现
char* NewStringUTF(JNIEnv* env,char* utf) {
    
    (*env)->hadlefunc(env);

    return utf;
}

void main() {
    //实例化结构体
    struct JavaNativeInterface jnf;
    jnf.hadlefunc = handleFunction;
    jnf.NewStringUTF = NewStringUTF;
    // 结构体指针
    JNIEnv e = &jnf;
    // 二级指针
    JNIEnv* env = &e;

    // 通过二级指针掉用函数
    char* res = (*env)->NewStringUTF(env, "模拟JNIEnv实现方式\n");

    // 打印
    printf("调用结果:%s", res);

    system("pause");
}

输出:

正在处理...
调用结果:模拟JNIEnv实现方式

结语

.h头文件的分析就到这里 ,关键是了解清楚 , native方法在C中生成函数名称的规则 , 以及对JNIEnv有个良好的认识 。

本文由老司机学院【动脑学院】特约提供 。

做一家受人尊敬的企业,做一位令人尊敬的老师

参考文献:
Java Native Interface 6.0 Specification

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容