JNI调用getPackageManager获取nativeLibraryDir出现弱引用已删除错误
问题原因
- 调用对象错误:
getApplicationInfo是PackageManager类的成员方法,代码中错误使用Context实例(即thiz)调用该方法,应该使用之前获取到的instantiatePackageManager(PackageManager实例)调用,方法调用时对象不匹配导致引用错乱。 - 参数类型不匹配:
getApplicationInfo第一个参数是Java的String类型,代码直接传入C原生的const char*类型的packageName,没有转换为jstring,非法的参数引用触发弱引用访问报错。 - 返回值为野指针:函数内构造的
appendedResult是栈上的局部std::string对象,函数执行结束后该对象会被销毁,返回其c_str()指向的是已释放的内存,后续访问会触发内存非法访问问题。 - JNI引用管理不规范:调用
GetStringUTFChars获取字符串后没有调用ReleaseStringUTFChars释放,会造成内存泄漏;同时未主动释放不需要的JNI局部引用,可能引发局部引用表溢出。
解决方案
- 修正方法调用对象:调用
getApplicationInfo时传入instantiatePackageManager实例而非thiz。 - 转换参数类型:将原生C字符串
packageName通过NewStringUTF转换为jstring后再作为参数传入。 - 修正返回值逻辑:修改函数返回值类型为
std::string直接返回字符串对象,避免返回野指针;如果必须返回const char*,需使用malloc分配内存拷贝字符串内容后返回,由调用方负责释放内存。 - 补全JNI引用释放逻辑:字符串使用完后调用
ReleaseStringUTFChars释放,主动删除不再使用的局部引用。
修正后代码示例
#include <string> #include <cstdlib> #include <cstring> // 方案1:返回std::string,无内存管理负担 std::string getNativeLibPath(JNIEnv* env, jobject thiz, const char* libraryName, const char* packageName) { // 转换包名为jstring jstring jPackageName = env->NewStringUTF(packageName); jclass contextClass = env->GetObjectClass(thiz); jmethodID getPackageManager = env->GetMethodID(contextClass, "getPackageManager", "()Landroid/content/pm/PackageManager;"); jobject instantiatePackageManager = env->CallObjectMethod(thiz, getPackageManager); jclass packageManagerClass = env->GetObjectClass(instantiatePackageManager); jmethodID getApplicationInfo = env->GetMethodID(packageManagerClass, "getApplicationInfo", "(Ljava/lang/String;I)Landroid/content/pm/ApplicationInfo;"); // 这里用PackageManager实例调用方法,不是thiz jobject instantiateApplicationInfo = env->CallObjectMethod(instantiatePackageManager, getApplicationInfo, jPackageName, 0); jclass applicationInfoClass = env->GetObjectClass(instantiateApplicationInfo); jfieldID nativeLibraryDir = env->GetFieldID(applicationInfoClass, "nativeLibraryDir", "Ljava/lang/String;"); auto jLibDir = (jstring) env->GetObjectField(instantiateApplicationInfo, nativeLibraryDir); const char* libDirStr = env->GetStringUTFChars(jLibDir, nullptr); std::string result = std::string(libDirStr) + "/" + libraryName; // 释放JNI引用 env->ReleaseStringUTFChars(jLibDir, libDirStr); env->DeleteLocalRef(jPackageName); env->DeleteLocalRef(jLibDir); env->DeleteLocalRef(applicationInfoClass); env->DeleteLocalRef(instantiateApplicationInfo); env->DeleteLocalRef(packageManagerClass); env->DeleteLocalRef(instantiatePackageManager); env->DeleteLocalRef(contextClass); return result; } // 方案2:如果必须返回const char*,调用方用完需free const char* getNativeLibPathC(JNIEnv* env, jobject thiz, const char* libraryName, const char* packageName) { std::string result = getNativeLibPath(env, thiz, libraryName, packageName); char* buf = (char*)malloc(result.size() + 1); strcpy(buf, result.c_str()); return buf; }
内容的提问来源于stack exchange,提问作者Arc Mods
相关产品推荐
相关产品推荐

