[JNI] 开发之旅 (2)解释jni helloworld实例

在上篇文章中已经介绍完jni开发的流程,实现了一个简单的hello world实例
接下来对这个实例中出现的关键词,函数进行解释。

.h头文件

通过javah生成的头文件

//引入jni.h文件
#include "jni.h"
/* Header for class com_honjane_jni_HelloJni */
//如果没有定义xxx ifndef后会成对跟上define
#ifndef _Included_com_honjane_jni_HelloJni
//定义xxx
#define _Included_com_honjane_jni_HelloJni
//跨平台定义方法  将C++代码以标准C形式输出
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_honjane_jni_HelloJni
 * Method:    helloWorld
 * Signature: ()Ljava/lang/String;
 */
 //JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。
 //jstring 为返回值
JNIEXPORT jstring JNICALL Java_com_honjane_jni_HelloJni_helloWorld
  (JNIEnv *, jclass);

#ifdef __cplusplus
}
#endif
#endif

通过上面解释,会有几个疑问jstring是什么类型JNIEnv是什么东西jclass代表什么

下面一一解释:

JNIEnv:

在c代码中JNIEnv是JNINativeInterface_ 结构体的指针别名, 在JNINativeInterface_结构体中 或者平时native代码中, 定义很多操作函数 。例如:

jstring (JNICALL *NewStringUTF) (JNIEnv *env, const char *utf);
jsize (JNICALL *GetStringUTFLength) (JNIEnv *env, jstring str);
NewStringUTF(JNIEnv *env, jstring str);
}

每个函数都需要一个JNIEnv指针,为什么需要呢 ?

第一:JNI接口定义好了就需要 , 在函数中仍然需要JNINativeInterface_结构体中的函数做处理
第二:区别对待C和C++ , jni是支持C/C++的,在jni.h头文件中 , 那么C++是怎么表示JNIEnv的呢 ?

在jni.h中c和c++有不同的处理:

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

上面代码可以看出在c中JNIEnv代表了JNINativeInterface结构体的指针
而在c++中JNIEnv是一个JNIEnv_结构体,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++环境下 ,JNINativeInterface_ *functions还是使用的JNINativeInterface_结构体指针调用函数, 使用FindClass函数时, 则不需要传入Env这个二级指针 ,因为C++是面向对象的语言 , 对FindClass(env,name)进行了封装,传入了this , 当前环境的指针,同理NewStringUTF都封装了不需要传染env指针

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

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

jclass与jobject

第一次接触jni可能会疑惑,这个jclass是什么东西,我们在定义jni方法时会看见两种不同的方法参数:

/*
 * Class:     com_honjane_jni_HelloJni
 * Method:    helloWord
 * Signature: ()Ljava.lang.String
 */
JNIEXPORT jstring JNICALL com_honjane_jni_HelloJni_helloWord
  (JNIEnv *, jobject);

/*
 * Class:     com_honjane_jni_HelloJni
 * Method:    helloWord
 * Signature: ()Ljava.lang.String
 */
JNIEXPORT jstring JNICALL com_honjane_jni_HelloJni_helloWord
  (JNIEnv *, jclass);

第二个参数的类型不一样,第一个方法为jobject,第二个方法为jclass;
jobject表明java层定义的这个方法是一个实例方法,指向这个实例;而下面的是jclass,表明是个静态方法,指向这个类的class

我们只要记住jobject代表了java层定义native方法的对应类的一个实例,而jclass代表这个类的class,如果native方法被static修饰,那么jni层第二个参数就是jclass,反之为jobject。很好理解,static方法属于类本身,而实例方法属于这个类的实例。

env->GetMethodID(jclasz,"callback","()V");

一个操作如GetMethodID,需要参数jclass,是一个类操作,因为它从一个类中获得Method的描述。

 env->CallVoidMethod(jobj,mid);

与此相反,CallVoidMethod需要参数jobject,这是一个实例操作,因为它调用属于某个实例的方法。在所有的JNI方法中jobject和实例操作的结合和jclass和类操作的结合保持一致。所以是很容易记住类操作与实例操作的不同的。

jni数据类型

与java数据类型相似,分为基本类型与引用类型:

primitive types ----基本数据类型,如:intfloatchar等基本类型
reference types----引用类型,如:类、实例、数组。

基本数据类型映射表:
这里写图片描述

基本数据类型在jni层可以直接使用的,不需要再作转换,比如传入jint,在jni层可以直接使用传入的jint

引用类型映射表

java类型native类型描述
Objectjobject任何对象
ClassjclassClass类对象
Stringjstring字符串
Object[]jobjectArray任何对象的数组
int[]jintArrayint数组
char[]jcharArray字符型对象


注意:

a.引用数据类型不能直接使用,需要根据JNI函数相应的转换成c或c++对应的数据类型才能使用
b.多维数组(包括二维)都是引用类型,需要使用 jobjectArray存取其值 ;
例如:二维整型数组就是指向一位数组的数组,其声明使用方式如下:

      //1.获得一维数组的类引用,即jintArray类型  
        jclass intArrayClass = env->FindClass("[I");   
      //2.构造一个指向jintArray类一维数组的对象数组,该对象数组初始大小为len
       jobjectArray obejctIntArray  =  env->NewObjectArray(len ,intArrayClass , NULL);  
       //这是一个二维数组的表示方式

jstring的意思就清楚,具体的一些域描述符,类描述符,方法描述符,在使用的时候再介绍。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值