3. JNI 基本数据类型与字符串传递
JNI 基本类型映射与字符串内存管理
JNI 的基本类型映射是 Java 与 C/C++ 互操作的基石。Java 的 int、boolean、float、double 在 native 层分别对应 jint、jboolean、jfloat、jdouble,它们都是值传递,直接按 C 类型使用即可。真正需要谨慎处理的是 jstring——它是不透明引用,必须通过 GetStringUTFChars 获取可读的 UTF-8 字符指针,使用完毕后必须调用 ReleaseStringUTFChars 释放,否则会造成内存泄漏。
关键实现:基本类型与字符串互转
下面给出一个完整的 native 方法,接收 Java 传入的 int、boolean、float、double 和 String,在 C++ 层处理后返回一个拼接后的字符串。重点观察 GetStringUTFChars 与 ReleaseStringUTFChars 的配对使用。
// native-lib.cpp
#include <jni.h>
#include <string>
#include <sstream>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_ndkdemo_MainActivity_processData(
JNIEnv* env,
jobject /* thiz */,
jint age,
jboolean flag,
jfloat ratio,
jdouble pi,
jstring name) {
// 1. 基本类型直接使用,无需转换
int cAge = age; // jint -> int
bool cFlag = (flag == JNI_TRUE); // jboolean -> bool
float cRatio = ratio; // jfloat -> float
double cPi = pi; // jdouble -> double
// 2. jstring 必须转换为 const char*
const char* utf8Name = env->GetStringUTFChars(name, nullptr);
if (utf8Name == nullptr) {
return env->NewStringUTF("GetStringUTFChars failed");
}
// 3. 在 C++ 层处理数据
std::ostringstream oss;
oss << "Name=" << utf8Name
<< ", Age=" << cAge
<< ", Flag=" << (cFlag ? "true" : "false")
<< ", Ratio=" << cRatio
<< ", Pi=" << cPi;
// 4. 释放 UTF 字符指针(必须!)
env->ReleaseStringUTFChars(name, utf8Name);
// 5. 将 C++ 字符串转回 jstring 返回
return env->NewStringUTF(oss.str().c_str());
}
对应的 Java 声明与调用:
public class MainActivity extends AppCompatActivity {
static {
System.loadLibrary("native-lib");
}
public native String processData(
int age, boolean flag, float ratio, double pi, String name);
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
String result = processData(25, true, 3.14f, 3.14159, "NDK");
Log.d("JNI", result);
// 预期输出: Name=NDK, Age=25, Flag=true, Ratio=3.14, Pi=3.14159
}
}
内存管理要点:
1.
2. 如果
3. 在
4. 若在 native 方法中提前 return,务必确保释放操作在 return 之前执行,或使用 RAII 封装。
1.
GetStringUTFChars 返回的指针指向 JVM 内部或临时拷贝的 UTF-8 缓冲区,必须用 ReleaseStringUTFChars 释放。2. 如果
GetStringUTFChars 返回 nullptr,说明内存分配失败,此时不能调用 ReleaseStringUTFChars。3. 在
ReleaseStringUTFChars 之后,utf8Name 指针立即失效,不能再访问。4. 若在 native 方法中提前 return,务必确保释放操作在 return 之前执行,或使用 RAII 封装。
验证与结果判读
| Java 传入值 | JNI 类型 | C++ 接收类型 | 预期输出片段 |
|---|---|---|---|
int 25 | jint | int | Age=25 |
boolean true | jboolean | bool | Flag=true |
float 3.14f | jfloat | float | Ratio=3.14 |
double 3.14159 | jdouble | double | Pi=3.14159 |
String "NDK" | jstring | const char* | Name=NDK |
运行后查看 Logcat 输出,若结果与预期一致,说明基本类型映射与字符串转换链路正确。若出现乱码,检查 GetStringUTFChars 的第二个参数是否传入了 nullptr(表示不关心是否拷贝),以及 Java 源文件编码是否为 UTF-8。
常见错误排查:
• 崩溃在 GetStringUTFChars:检查传入的
• 内存持续增长:检查是否遗漏了
• 返回字符串乱码:确认
• 崩溃在 GetStringUTFChars:检查传入的
jstring 是否为 nullptr,Java 侧是否传入了 null。• 内存持续增长:检查是否遗漏了
ReleaseStringUTFChars,或异常路径下未释放。• 返回字符串乱码:确认
NewStringUTF 接收的是有效的 UTF-8 编码字符串,且生命周期覆盖调用期间。