JNI Types and Data Structures

本文详细介绍了Java Native Interface (JNI) 如何将Java类型映射为本地C类型,并阐述了JNI中基本类型、引用类型、字段及方法ID、类型签名等核心概念。

http://java.sun.com/j2se/1.5.0/docs/guide/jni/spec/types.html#wp16432

Contents | Previous | Next

Java Native Interface Specification

JNI Types and Data Structures


Chapter   3

This chapter discusses how the JNI maps Java types to native C types.

Primitive Types

Table 3-1 describes Java primitive types and their machine-dependent native equivalents.

 

Table 3-1 Primitive Types and Native Equivalents
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
N/A

 

The following definition is provided for convenience.

#define JNI_FALSE  0 
#define JNI_TRUE   1 

The jsize integer type is used to describe cardinal indices and sizes:

typedef jint jsize; 

Reference Types

The JNI includes a number of reference types that correspond to different kinds of Java objects. JNI reference types are organized in the hierarchy shown in Figure 3-1.

 

The top of the heirarchy is jobject. Subclasses of jobject are jclass, jstring, jarray and jthrowable. Subclasses of jarray are jobjectArray, jbooleanArray, jbyteArray, jcharArray, jshortArray, jintArray, jlongArray, jfloatArray, jdoubleArray.

 

Figure 3-1 Reference Type Hierarchy 

In C, all other JNI reference types are defined to be the same as jobject. For example:

typedef jobject jclass; 

In C++, JNI introduces a set of dummy classes to enforce the subtyping relationship. For example:

class _jobject {}; 
class _jclass : public _jobject {}; 
... 
typedef _jobject *jobject; 
typedef _jclass *jclass; 

Field and Method IDs

Method and field IDs are regular C pointer types:

struct _jfieldID;              /* opaque structure */ 
typedef struct _jfieldID *jfieldID;   /* field IDs */ 
 
struct _jmethodID;              /* opaque structure */ 
typedef struct _jmethodID *jmethodID; /* method IDs */ 

The Value Type

The jvalue union type is used as the element type in argument arrays. It is declared as follows:

typedef union jvalue { 
    jboolean z; 
    jbyte    b; 
    jchar    c; 
    jshort   s; 
    jint     i; 
    jlong    j; 
    jfloat   f; 
    jdouble  d; 
    jobject  l; 
} jvalue; 

Type Signatures

The JNI uses the Java VM’s representation of type signatures. Table 3-2 shows these type signatures.

 

Table 3-2 Java VM Type Signatures
Type Signature
Java Type
Z
boolean
B
byte
C
char
S
short
I
int
J
long
F
float
D
double
L fully-qualified-class ;
fully-qualified-class
[ type
type[]
( arg-types ) ret-type
method type

 

For example, the Java method:

long f (int n, String s, int[] arr); 

has the following type signature:

(ILjava/lang/String;[I)J 

Modified UTF-8 Strings

The JNI uses modified UTF-8 strings to represent various string types. Modified UTF-8 strings are the same as those used by the Java VM. Modified UTF-8 strings are encoded so that character sequences that contain only non-null ASCII characters can be represented using only one byte per character, but all Unicode characters can be represented.

All characters in the range /u0001 to /u007F are represented by a single byte, as follows:

 

0

 

bits 6-0

The seven bits of data in the byte give the value of the character represented.

The null character ('/u0000') and characters in the range '/u0080' to '/u07FF' are represented by a pair of bytes x and y:

x:

 

1

 

1

 

0

 

bits 10-6
y:

 

1

 

0

 

bits 5-0

The bytes represent the character with the value ((x & 0x1f) << 6) + (y & 0x3f).

Characters in the range '/u0800' to '/uFFFF' are represented by 3 bytes x, y, and z:

x:

 

1

 

1

 

1

 

0

 

bits 15-12
y:

 

1

 

0

 

bits 11-6
z:

 

1

 

0

 

bits 5-0

The character with the value ((x & 0xf) << 12) + ((y & 0x3f) << 6) + (z & 0x3f) is represented by the bytes.

Characters with code points above U+FFFF (so-called supplementary characters) are represented by separately encoding the two surrogate code units of their UTF-16 representation. Each of the surrogate code units is represented by three bytes. This means, supplementary characters are represented by six bytes, uvwxy, and z:

u:

 

1

 

1

 

1

 

0

 

1

 

1

 

0

 

1
v:

 

1

 

0

 

1

 

0

 

(bits 20-16) - 1
w:

 

1

 

0

 

bits 15-10
x:

 

1

 

1

 

1

 

0

 

1

 

1

 

0

 

1
y:

 

1

 

0

 

1

 

1

 

bits 9-6
z:

 

1

 

0

 

bits 5-0

The character with the value 0x10000+((v&0x0f)<<16)+((w&0x3f)<<10)+(y&0x0f)<<6)+(z&0x3f) is represented by the six bytes.

The bytes of multibyte characters are stored in the class file in big-endian (high byte first) order.

There are two differences between this format and the standard UTF-8 format. First, the null character (char)0 is encoded using the two-byte format rather than the one-byte format. This means that modified UTF-8 strings never have embedded nulls. Second, only the one-byte, two-byte, and three-byte formats of standard UTF-8 are used. The Java VM does not recognize the four-byte format of standard UTF-8; it uses its own two-times-three-byte format instead.

For more information regarding the standard UTF-8 format, see section 3.9 Unicode Encoding Forms of The Unicode Standard, Version 4.0.

 


Contents | Previous | Next 

 

<think>嗯,用户现在想了解如何在JNI中调用DataCallback的onData方法。首先,我需要回忆一下JNI的基本知识。JNIJava本地接口,允许Java代码和本地代码(如C/C++)互相调用。用户提到的DataCallback应该是一个Java接口,而onData是其中的方法。 首先,我需要确定用户的具体需求。他们可能正在开发一个Android应用,需要从本地代码(比如C++)触发Java层的回调方法。这种情况下,正确使用JNI来调用Java方法就很重要了。 接下来,我需要考虑调用Java方法的基本步骤。通常,这包括获取类引用、方法ID,然后使用JNI函数进行调用。对于非静态方法,需要有一个对象实例。如果是接口方法,可能还需要确认该对象是否已经正确实现接口。 用户提供的例子可能涉及到回调,所以可能是在本地代码中持有Java对象的引用,然后在适当的时候调用其onData方法。这里需要注意的是JNI环境的正确获取,尤其是在多线程情况下,可能需要附加到JVM线程。 另外,要考虑异常处理。JNI调用可能会抛出异常,必须在本地代码中检查并处理,否则可能导致崩溃。例如,在调用方法后使用ExceptionCheck来查看是否有异常发生。 还有内存管理的问题。本地代码中创建的局部引用需要及时删除,避免内存泄漏。特别是当在循环中创建大量局部引用时,应该使用DeleteLocalRef或者确保在合适的时机释放。 用户可能还需要传递参数给onData方法。假设onData方法有一个byte数组作为参数,那么在本地代码中需要构造一个jbyteArray对象,并将其传递给方法。这涉及到如何将本地数据转换为Java数组,并正确管理其内存。 另外,考虑性能优化。频繁的JNI调用可能影响性能,特别是在处理大量数据时。可能需要批量处理数据,减少JNI调用的次数,或者使用直接缓冲区等方法提高效率。 还有,回调接口的生命周期管理。确保在Java对象被回收后,本地代码不再持有其引用,避免野指针或无效引用导致的崩溃。可能需要使用GlobalRef来维持长期引用,并在不再需要时释放。 用户可能遇到的问题包括方法签名错误、未能正确获取方法ID、对象引用错误等。需要指导他们如何正确获取方法签名,例如使用javap工具获取准确的方法签名,避免手动编写出错。 此外,跨线程调用时需要注意JNIEnv的正确使用,因为每个线程的JNIEnv是不同的。需要使用AttachCurrentThread获取当前线程的JNIEnv,并在完成后Detach,特别是在多线程环境中。 最后,可能需要提供一个完整的示例代码,展示如何从JNI调用onData方法,包括获取类、方法ID、调用方法以及异常处理和资源释放的步骤。这有助于用户更直观地理解和应用。</think>--- 在 JNIJava Native Interface)中调用 Java 的 `DataCallback` 接口的 `onData` 方法,需要 **获取方法 ID、构造参数并触发回调**。以下是详细步骤及注意事项: --- ### **1. 定义 Java 回调接口** 假设 `DataCallback` 是 Java 层的接口,定义如下: ```java public interface DataCallback { void onData(byte[] data, int length); // 示例方法参数 } ``` --- ### **2. JNI 调用步骤** #### **步骤 1:获取回调类和方法 ID** 在本地代码(C/C++)中,首先获取 `DataCallback` 类和 `onData` 方法 ID: ```cpp // 全局缓存(避免重复查找) jclass g_dataCallbackClass = nullptr; jmethodID g_onDataMethodID = nullptr; JNIEXPORT void JNICALL Java_com_example_NativeLib_init(JNIEnv* env, jobject thiz, jobject callback) { // 获取 DataCallback 类引用 jclass localClass = env->GetObjectClass(callback); g_dataCallbackClass = (jclass)env->NewGlobalRef(localClass); // 提升为全局引用 env->DeleteLocalRef(localClass); // 释放局部引用 // 获取 onData 方法 ID(参数类型需与 Java 方法签名匹配) const char* methodName = "onData"; const char* methodSignature = "([BI)V"; // void(byte[], int) g_onDataMethodID = env->GetMethodID(g_dataCallbackClass, methodName, methodSignature); // 检查方法是否存在 if (g_onDataMethodID == nullptr) { // 处理错误:方法未找到 } } ``` #### **步骤 2:触发回调** 在需要回调时调用 `onData` 方法: ```cpp void triggerCallback(JNIEnv* env, jobject callback, jbyteArray data, jint length) { // 确保回调对象和方法 ID 有效 if (callback == nullptr || g_onDataMethodID == nullptr) { return; } // 调用 onData 方法 env->CallVoidMethod(callback, g_onDataMethodID, data, length); // 检查是否发生异常 if (env->ExceptionCheck()) { env->ExceptionDescribe(); // 打印异常信息(调试用) env->ExceptionClear(); // 清除异常(避免崩溃) } } ``` --- ### **3. 参数构造示例** 若需传递 `byte[]` 数据到 Java 层: ```cpp // 示例:将 C/C++ 数据转换为 jbyteArray jbyteArray createJavaByteArray(JNIEnv* env, const uint8_t* data, int length) { jbyteArray javaArray = env->NewByteArray(length); env->SetByteArrayRegion(javaArray, 0, length, (const jbyte*)data); return javaArray; } // 使用示例 uint8_t rawData[] = {0x01, 0x02, 0x03}; jbyteArray javaData = createJavaByteArray(env, rawData, sizeof(rawData)); triggerCallback(env, callback, javaData, sizeof(rawData)); env->DeleteLocalRef(javaData); // 释放局部引用 ``` --- ### **4. 关键注意事项** 1. **线程安全**: - JNIEnv 是线程相关的,**不可跨线程直接使用**。若在子线程中调用,需通过 `AttachCurrentThread` 获取当前线程的 JNIEnv: ```cpp JavaVM* g_jvm; // 需在 JNI_OnLoad 中初始化 JNIEnv* env; g_jvm->AttachCurrentThread(&env, nullptr); triggerCallback(env, callback, data, length); g_jvm->DetachCurrentThread(); // 可选,根据场景决定 ``` 2. **引用管理**: - **局部引用**(如 `jbyteArray`)需手动释放(`env->DeleteLocalRef`)。 - **全局引用**(如 `g_dataCallbackClass`)在不再使用时需释放: ```cpp env->DeleteGlobalRef(g_dataCallbackClass); ``` 3. **方法签名**: - 使用 `javap -s com.example.DataCallback` 获取准确方法签名: ``` Compiled from "DataCallback.java" public interface com.example.DataCallback { public abstract void onData(byte[], int); descriptor: ([BI)V } ``` 4. **异常处理**: - 在调用 `CallVoidMethod` 后必须检查异常,否则可能导致崩溃。 --- ### **5. 完整流程示意图** ``` Java 层初始化 Native 模块 → 传递 DataCallback 对象到 JNIJNI 缓存 DataCallback 类和方法 ID ↓ 本地代码生成数据 → 构造 jbyteArray → 调用 onData 方法 ↓ Java 层接收数据并处理 ``` --- ### **常见问题** - **崩溃问题**:未正确附加线程(如子线程未调用 `AttachCurrentThread`)。 - **回调不触发**:方法签名错误或全局引用未正确缓存。 - **内存泄漏**:未释放全局引用或局部引用。 --- 通过以上步骤,可安全地在 JNI 中调用 Java 回调接口方法。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值