Ahao's Technical Blog Ahao's Technical Blog
首页
  • 001.基础篇
  • 002.玩转AOSP篇
  • 003.学穿Binder篇
  • 004.基础组件篇
  • 005.系统启动过程分析
  • 006.Hal开发入门与实践
  • 007.显示系统
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

阿豪讲Framework

不积跬步无以至千里
首页
  • 001.基础篇
  • 002.玩转AOSP篇
  • 003.学穿Binder篇
  • 004.基础组件篇
  • 005.系统启动过程分析
  • 006.Hal开发入门与实践
  • 007.显示系统
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • 基础篇

    • 001.Ubuntu 使用快速入门
    • 002.Make 构建工具入门
    • 003.Android 平台如何编译执行 C C++ 可执行程序
    • 004.Linux Shell 脚本编程入门1——核心基础语法
    • 005.Linux Shell 脚本编程入门2——脚本自动化基础
    • 006.JNI 编程上手指南之 JavaVM 详解
    • 007.JNI 编程上手指南之 JNI 数据类型
    • 008.JNI 编程上手指南之描述符
    • 009.JNI 编程上手指南之 JNIEnv 详解
      • 1. JNIEnv 是什么
      • 2. 如何获取到 JNIEnv
      • 3. JNIEnv 内部函数分类
    • 010.JNI 编程上手指南之 JavaVM 详解
    • 011.JNI 编程上手指南之字符串处理
    • 012.JNI 编程上手指南之数组访问
    • 013.JNI 编程上手指南之 Native 访问 Java
    • 014.JNI 编程上手指南之异常处理
    • 015.JNI 编程上手指南之从内存角度再看引用类型
    • 016.JNI 编程上手指南之 JNI 调用性能优化
    • 017.JNI 编程上手指南之多线程
    • 018.理解 C++ 的 Memory Order
  • 玩转AOSP篇

  • 学穿Binder篇

  • 基础组件篇

  • 系统启动过程分析

  • Hal开发入门与实践

  • 显示系统

  • Framework
  • 基础篇
阿豪
2023-07-03
目录

009.JNI 编程上手指南之 JNIEnv 详解

# 1. JNIEnv 是什么

JNIEnv 即 Java Native Interface Environment,Java 本地编程接口环境。JNIEnv 内部定义了很多函数用于简化我们的 JNI 编程。

JNI 把 Java 中的所有对象或者对象数组当作一个 C 指针传递到本地方法中,这个指针指向 JVM 中的内部数据结构(对象用jobject来表示,而对象数组用jobjectArray或者具体是基本类型数组),而内部的数据结构在内存中的存储方式是不可见的。只能从 JNIEnv 指针指向的函数表中选择合适的 JNI 函数来操作JVM 中的数据结构。

在 C 语言中,JNIEnv 是一个指向 JNINativeInterface_ 结构体的指针:

#ifdef __cplusplus
typedef JNIEnv_ JNIEnv;
#else
typedef const struct JNINativeInterface_ *JNIEnv; // C 语言
#endif

struct JNINativeInterface_ {
    void *reserved0;
    void *reserved1;
    void *reserved2;

    void *reserved3;
    jint (JNICALL *GetVersion)(JNIEnv *env);

    jclass (JNICALL *DefineClass)
      (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
       jsize len);

    jstring (JNICALL *NewStringUTF)
      (JNIEnv *env, const char *utf);

    //省略其他函数指针
    //......
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

JNINativeInterface_ 结构体中定义了非常多的函数指针,这些函数用于简化我们的 JNI 编程。C 语言中,JNIEnv 中函数的使用方式如下:

//JNIEnv * env
// env 的实际类型是 JNINativeInterface_**
(*env)->NewStringUTF(env,"Hello from JNI !");
1
2
3

在 C++ 代码中,JNIEnv 是一个 JNIEnv_ 结构体:

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

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);
    }
    jfieldID FromReflectedField(jobject field) {
        return functions->FromReflectedField(this,field);
    }

    jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) {
        return functions->ToReflectedMethod(this, cls, methodID, isStatic);
    }

    jclass GetSuperclass(jclass sub) {
        return functions->GetSuperclass(this, sub);
    }
    //省略其他函数
    //......
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37

JNIEnv_ 结构体中同样定义了非常多的成员函数,这些函数用于简化我们的 JNI 编程。C++ 语言中,JNIEnv 中函数的使用方式如下:

//JNIEnv * env
// env 的实际类型是 JNIEnv_*
env->NewstringUTF ( "Hello from JNI ! ");
1
2
3

# 2. 如何获取到 JNIEnv

对于单线程的情况,我们可以直接通过 JNI 方法传入的参数获取到 JNIEnv

// 第一个参数就是 JNIEnv
JNIEXPORT jstring JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject obj)
{
    return (*env)->NewStringUTF(env,"Hello from JNI !");
}
1
2
3
4
5

对于多线程的情况,首先我们要知道,JNIEnv 是一个线程作用域的变量,不能跨线程传递,不同线程的 JNIEnv 彼此独立。那如何在不同的线程中获取到 JNIEnv 呢:

//定义全局变量
//JavaVM 是一个结构体,用于描述 Java 虚拟机,后面会讲
JavaVM* gJavaVM;

JNIEXPORT jstring JNICALL Java_HelloJNI_sayHello(JNIEnv *env, jobject obj)
{   
    //线程不允许共用env环境变量,但是JavaVM指针是整个jvm共用的,所以可以通过下面的方法保存JavaVM指针,在线程中使用
    env->GetJavaVM(&gJavaVM);
    return (*env)->NewStringUTF(env,"Hello from JNI !");
}

//假设这是一个工具函数,可能被多个线程调用
void util_xxx()
{
    JNIEnv *env;
    //从全局的JavaVM中获取到环境变量
    gJavaVM->AttachCurrentThread(&env,NULL);

    //就可以使用 JNIEnv 了

    //最后需要做清理操作
    gJavaVM->DetachCurrentThread();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 3. JNIEnv 内部函数分类

JNIEnv 中定义的函数可以分为以下几类:

函数名 功能
FindClass 用于获取类
GetObjectClass 通过对象获取这个类
NewGlobalRef 创建 obj 参数所引用对象的新全局引用
NewObject 构造新 Java 对象
NewString 利用 Unicode 字符数组构造新的 java.lang.String 对象
NewStringUTF 利用 UTF-8 字符数组构造新的 java.lang.String 对象
New<Type>Array 创建类型为Type的数组对象
Get<Type>Field 获取类型为Type的字段
Set<Type>Field 设置类型为Type的字段的值
GetStatic<Type>Field 获取类型为Type的static的字段
SetStatic<Type>Field 设置类型为Type的static的字段的值
Call<Type>Method 调用返回类型为Type的方法
CallStatic<Type>Method 调用返回值类型为Type的static方法

相关的函数不止上面的这些,这些函数的介绍和使用方法,我们可以在开发过程中参考官方文档 https://docs.oracle.com/en/java/javase/11/docs/specs/jni/index.html

008.JNI 编程上手指南之描述符
010.JNI 编程上手指南之 JavaVM 详解

← 008.JNI 编程上手指南之描述符 010.JNI 编程上手指南之 JavaVM 详解→

最近更新
01
如何调试 SurfaceFlinger
10-05
02
SurfaceFlinger 概述
10-05
03
HWC 接口分析
10-05
更多文章>
Theme by Vdoing | Copyright © 2020-2025 AHao Framework | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式