JNI调用Java加密方法致安卓设备背光异常闪退问题求助
问题排查与解决
你的安卓应用闪退问题,核心出在JNI调用Java方法时用错了调用对象,再加上几个JNI使用的细节没处理到位,直接导致了崩溃。我帮你拆解下问题,再给出具体的修复方案:
最关键的错误:调用对象不匹配
你在CPP代码里写了env->CallObjectMethod(obj, encrypt_func, a, key),这里的obj是NativeEncryptionUtils类的实例(因为这个JNI方法本身就是NativeEncryptionUtils的encrypt方法),但encryptdata方法是属于NativeDES类的——你用A类的对象去调用B类的实例方法,这完全不匹配,不崩溃才怪!
两种修复方案,按需选择
方案1:把encryptdata改成静态方法(推荐,JNI调用更简单)
先修改Java端的NativeDES类,给encryptdata加上static修饰符:
public static byte[] encryptdata(byte[] data1, String key) { byte[] clear = null; try { SecretKeySpec sc = new SecretKeySpec(key.getBytes("UTF-8"),"DESede"); Cipher cipher = Cipher.getInstance("DESede"); cipher.init(Cipher.ENCRYPT_MODE,sc); clear = cipher.doFinal(data1); } catch (Exception e) { e.printStackTrace(); } return clear; }
然后修改JNI代码,改用CallStaticObjectMethod调用静态方法,不需要创建NativeDES的实例:
JNIEXPORT jbyteArray JNICALL Java_com_airtel_agilelabs_utils_NativeEncryptionUtils_encrypt( JNIEnv* env, jobject obj, jobject context, jbyteArray data) { // 先检查输入参数是否为空,避免后续空指针操作 if (data == nullptr) { return nullptr; } jbyteArray input_data = data; jstring key = env->NewStringUTF("[key]"); // 检查字符串分配是否成功 if (key == nullptr) { return nullptr; } // 查找NativeDES类,注意包名要完全匹配 jclass des_class = env->FindClass("com/test/test1/utils/NativeDES"); if (des_class == nullptr) { env->DeleteLocalRef(key); return nullptr; // 类找不到,大概率是包名写错了 } // 获取静态方法ID,方法签名要和Java方法完全对应 jmethodID encrypt_func = env->GetStaticMethodID(des_class, "encryptdata", "([BLjava/lang/String;)[B"); if (encrypt_func == nullptr) { env->DeleteLocalRef(des_class); env->DeleteLocalRef(key); return nullptr; // 方法找不到,检查方法名或签名 } // 调用静态方法,这里用类对象des_class作为调用者 jobject result = env->CallStaticObjectMethod(des_class, encrypt_func, input_data, key); jbyteArray byte_result = (jbyteArray) result; // 释放本地引用,避免内存泄漏 env->DeleteLocalRef(des_class); env->DeleteLocalRef(key); return byte_result; }
方案2:保留实例方法,先创建NativeDES对象
如果不想改成静态方法,那你需要先通过JNI创建NativeDES的实例,再用这个实例去调用encryptdata:
JNIEXPORT jbyteArray JNICALL Java_com_airtel_agilelabs_utils_NativeEncryptionUtils_encrypt( JNIEnv* env, jobject obj, jobject context, jbyteArray data) { if (data == nullptr) { return nullptr; } jbyteArray input_data = data; jstring key = env->NewStringUTF("[key]"); if (key == nullptr) { return nullptr; } jclass des_class = env->FindClass("com/test/test1/utils/NativeDES"); if (des_class == nullptr) { env->DeleteLocalRef(key); return nullptr; } // 获取NativeDES的无参构造方法ID jmethodID constructor = env->GetMethodID(des_class, "<init>", "()V"); if (constructor == nullptr) { env->DeleteLocalRef(des_class); env->DeleteLocalRef(key); return nullptr; } // 创建NativeDES的实例对象 jobject des_obj = env->NewObject(des_class, constructor); if (des_obj == nullptr) { env->DeleteLocalRef(des_class); env->DeleteLocalRef(key); return nullptr; } // 获取实例方法ID jmethodID encrypt_func = env->GetMethodID(des_class, "encryptdata", "([BLjava/lang/String;)[B"); if (encrypt_func == nullptr) { env->DeleteLocalRef(des_obj); env->DeleteLocalRef(des_class); env->DeleteLocalRef(key); return nullptr; } // 注意!这里要用des_obj(NativeDES的实例)作为调用者,而不是原来的obj jobject result = env->CallObjectMethod(des_obj, encrypt_func, input_data, key); jbyteArray byte_result = (jbyteArray) result; // 释放所有本地引用 env->DeleteLocalRef(des_obj); env->DeleteLocalRef(des_class); env->DeleteLocalRef(key); return byte_result; }
额外的JNI注意事项,避免后续踩坑
- 方法签名要精准:你用的
([BLjava/lang/String;)[B是对的,对应byte[] encryptdata(byte[], String),如果后续修改Java方法的参数或返回值,一定要同步更新签名。 - 释放本地引用:JNI中创建的
jclass、jstring、jobject都是本地引用,不用了必须手动释放,否则会内存泄漏,严重时也会引发崩溃。 - 空指针检查不能少:所有JNI调用的返回值(比如
FindClass、GetMethodID)都要检查是否为nullptr,不然一步错就会导致整个应用崩溃。 - 无用参数处理:你的JNI方法里有个
jobject context参数,但代码里没用到,如果不需要可以直接删掉,省得混淆。
关于CallObjectMethod和其他调用函数的选择
- 如果Java方法是实例方法(非static),必须用
CallObjectMethod,而且调用对象必须是该类的实例。 - 如果Java方法是静态方法,必须用
CallStaticObjectMethod,调用对象是该类的jclass对象。 - 你之前的错误就是把A类的实例当成B类的实例去调用方法,完全不匹配,这是JNI调用里的低级错误,但也是新手常踩的坑。
内容的提问来源于stack exchange,提问作者Divesh Gupta
相关产品推荐
相关产品推荐

