JNI调用Android Java方法失败:native代码无法调用MainActivity方法
问题排查与解决方案
核心问题分析
你的Native代码调用Java方法失败(customRequestMethod为NULL),主要由以下几个原因导致:
- 类对象获取错误:
GetObjectClass(app->activity->clazz)可能拿到的是NativeActivity的类,而非你自定义的MainActivity类,自然找不到子类中新增的方法。 - 调用时机过早:
android_main启动时直接调用权限申请,此时MainActivity的初始化可能还未完成,Activity实例未就绪。 - Manifest配置缺失:未正确将
MainActivity配置为启动Activity,导致实际运行的是原生NativeActivity,没有自定义方法的实现。
分步解决方案
1. 修正AndroidManifest配置
确保Manifest中声明MainActivity为启动Activity,并配置Native库依赖:
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <!-- 必须配置,关联Native库 --> <meta-data android:name="android.app.lib_name" android:value="你的Native库名称" /> </activity> </application>
2. 修正JNI类查找逻辑
直接通过全类名查找MainActivity的Class(注意将包名中的.替换为/):
// 替换原有的GetObjectClass调用 jclass activityClass = env->FindClass("com/google/sample/simcode/MainActivity");
3. 调整权限申请时机
避免在android_main启动时直接调用,改为在Activity窗口初始化完成后触发(通过NDK的事件回调):
#include <android/native_activity.h> static void handleAppCmd(struct android_app* app, int32_t cmd) { switch (cmd) { case APP_CMD_INIT_WINDOW: // 窗口初始化完成,此时Activity已就绪 callJavaRequestPermissions(app); break; // 其他事件处理 default: break; } } void android_main(struct android_app* app) { // 设置事件回调 app->onAppCmd = handleAppCmd; // 事件循环,保证Native层正常运行 int events; struct android_poll_source* source; while (ALooper_pollAll(-1, nullptr, &events, (void**)&source) >= 0) { if (source != nullptr) { source->process(app, source); } } }
4. 添加JNI错误日志排查
加入日志输出,方便定位问题:
#include <android/log.h> #define LOG_TAG "JNI_PERM" #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) // 在类查找失败时输出日志 if (activityClass == nullptr) { LOGE("找不到MainActivity类"); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } return; } // 在方法查找失败时输出日志 if (customRequestMethod == nullptr) { LOGE("找不到customRequestPermissions方法"); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } return; }
同时在build.gradle中开启日志支持:
android { defaultConfig { externalNativeBuild { cmake { arguments "-DANDROID_LOG_ENABLED=ON" } } } }
修改后的完整JNI代码
#include <android/native_activity.h> #include <android/log.h> #include <jni.h> #define LOG_TAG "JNI_PERM" #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__) void callJavaRequestPermissions(struct android_app* app) { JNIEnv* env = nullptr; app->activity->vm->AttachCurrentThread(&env, nullptr); jclass activityClass = env->FindClass("com/google/sample/simcode/MainActivity"); if (activityClass == nullptr) { LOGE("找不到MainActivity类"); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } app->activity->vm->DetachCurrentThread(); return; } jmethodID customRequestMethod = env->GetMethodID(activityClass, "customRequestPermissions", "([Ljava/lang/String;I)V"); if (customRequestMethod == nullptr) { LOGE("找不到customRequestPermissions方法"); if (env->ExceptionCheck()) { env->ExceptionDescribe(); env->ExceptionClear(); } env->DeleteLocalRef(activityClass); app->activity->vm->DetachCurrentThread(); return; } jobjectArray permissions = env->NewObjectArray(1, env->FindClass("java/lang/String"), nullptr); jstring permission = env->NewStringUTF("android.permission.WRITE_EXTERNAL_STORAGE"); env->SetObjectArrayElement(permissions, 0, permission); // 调用Java方法,传入Activity实例 env->CallVoidMethod(app->activity->clazz, customRequestMethod, permissions, 101); // 释放局部引用 env->DeleteLocalRef(permission); env->DeleteLocalRef(permissions); env->DeleteLocalRef(activityClass); app->activity->vm->DetachCurrentThread(); } static void handleAppCmd(struct android_app* app, int32_t cmd) { switch (cmd) { case APP_CMD_INIT_WINDOW: callJavaRequestPermissions(app); break; default: break; } } void android_main(struct android_app* app) { app->onAppCmd = handleAppCmd; int events; struct android_poll_source* source; while (ALooper_pollAll(-1, nullptr, &events, (void**)&source) >= 0) { if (source != nullptr) { source->process(app, source); } } }
内容的提问来源于stack exchange,提问作者خلیل ترابی
相关产品推荐
相关产品推荐

