Android JNI,调用 getMethodID 导致应用程序崩溃
我正在尝试在Android应用中使用JNI。我的代码正确编译并启动了应用程序,但是当我想在我的C ++代码中调用Java方法(来自调用类)时,该应用程序迅速崩溃,说它找不到该方法。
您将必须原谅代码,这有点混乱,因为我尝试了很多事情,它也与反应的东西混合在一起,但是JNI Crash与它无关。
Java模块:
package com.jsirnwalletcore;
import androidx.annotation.NonNull;
import androidx.annotation.Keep;
import android.util.Log;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
@Keep
class WalletCoreModule extends ReactContextBaseJavaModule {
public static final String NAME = "WalletCore";
private static native void initialize(long jsiPtr);
private SharedPreferences prefs;
public WalletCoreModule(ReactApplicationContext context) {
super(context);
this.prefs = context.getSharedPreferences("WALLETCORE", Context.MODE_PRIVATE);
}
@NonNull
@Override
public String getName() {
return NAME;
}
public String getSeed() {
return "HARDCODED SEED";
}
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean install() {
try {
String seed = getSeed();
Log.w(NAME, "ROPO CALLING GET SEED: " + seed);
System.loadLibrary("jsi-rn-wallet-core");
ReactApplicationContext context = getReactApplicationContext();
initialize(context.getJavaScriptContextHolder().get());
return true;
} catch(Exception exception) {
return false;
}
}
}
cpp-adapter.cpp:
#include <jni.h>
#include "installer.hpp"
#include "logs.h"
#include <string>
#include "pthread.h"
#include <sys/types.h>
#include <jsi/jsi.h>
JavaVM *java_vm;
jclass java_class;
jobject java_object;
/**
* A simple callback function that allows us to detach current JNI Environment
* when the thread
* See https://stackoverflow.com/a/30026231 for detailed explanation
*/
void DeferThreadDetach(JNIEnv *env)
{
static pthread_key_t thread_key;
// Set up a Thread Specific Data key, and a callback that
// will be executed when a thread is destroyed.
// This is only done once, across all threads, and the value
// associated with the key for any given thread will initially
// be NULL.
static auto run_once = []
{
const auto err = pthread_key_create(&thread_key, [](void *ts_env)
{
if (ts_env) {
java_vm->DetachCurrentThread();
} });
if (err)
{
// Failed to create TSD key. Throw an exception if you want to.
}
return 0;
}();
// For the callback to actually be executed when a thread exits
// we need to associate a non-NULL value with the key on that thread.
// We can use the JNIEnv* as that value.
const auto ts_env = pthread_getspecific(thread_key);
if (!ts_env)
{
if (pthread_setspecific(thread_key, env))
{
// Failed to set thread-specific value for key. Throw an exception if you want to.
}
}
}
/**
* Get a JNIEnv* valid for this thread, regardless of whether
* we're on a native thread or a Java thread.
* If the calling thread is not currently attached to the JVM
* it will be attached, and then automatically detached when the
* thread is destroyed.
*
* See https://stackoverflow.com/a/30026231 for detailed explanation
*/
JNIEnv *GetJniEnv()
{
JNIEnv *env = nullptr;
// We still call GetEnv first to detect if the thread already
// is attached. This is done to avoid setting up a DetachCurrentThread
// call on a Java thread.
// g_vm is a global.
auto get_env_result = java_vm->GetEnv((void **)&env, JNI_VERSION_1_6);
if (get_env_result == JNI_EDETACHED)
{
if (java_vm->AttachCurrentThread(&env, NULL) == JNI_OK)
{
DeferThreadDetach(env);
}
else
{
// Failed to attach thread. Throw an exception if you want to.
}
}
else if (get_env_result == JNI_EVERSION)
{
// Unsupported JNI version. Throw an exception if you want to.
}
return env;
}
void tempInstall(jsi::Runtime &rt)
{
auto testFn = jsi::Function::createFromHostFunction(
rt,
jsi::PropNameID::forAscii(rt, "testFn"),
0,
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
{
JNIEnv *jniEnv = GetJniEnv();
java_class = jniEnv->GetObjectClass(java_object);
jmethodID get = jniEnv->GetMethodID(java_class, "getSeed", "()Ljava/lang/String;");
// jobject result = jniEnv->CallObjectMethod(java_object, get);
// const char *str = jniEnv->GetStringUTFChars((jstring)result, NULL);
// LOGW("ROPO GOT: %s", str);
return {};
});
rt.global().setProperty(rt, "testFn", std::move(testFn));
}
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
{
java_vm = jvm;
LOGW("ROPO JNI_OnLoad CALLED");
return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT void JNICALL
Java_com_jsirnwalletcore_WalletCoreModule_initialize(JNIEnv *env, jobject thiz, jlong jsi)
{
auto rt = reinterpret_cast<jsi::Runtime *>(jsi);
// jclass clazz = env->GetObjectClass(thiz);
// jmethodID getNameMID = env->GetMethodID(clazz, "getName", "()Ljava/lang/String;");
// jstring jClassName = (jstring)env->CallObjectMethod(thiz, getNameMID);
// std::string classString = jstringToString(env, jClassName);
// LOGW("###########ROPO############");
// LOGW("RETRIEVED STRING: %s", classString.c_str());
// LOGW("###########ROPO############");
// jmethodID retrieveMethodID = env->GetMethodID(clazz, "getSeed", "()Ljava/lang/String;");
// if (retrieveMethodID == NULL)
// {
// LOGW("ROPO methodID not found!");
// }
// else
// {
// jstring myString = (jstring)env->CallObjectMethod(thiz, retrieveMethodID);
// std::string retrievedString = jstringToString(env, myString);
// LOGW("###########ROPO############");
// LOGW("RETRIEVED STRING: %s", retrievedString.c_str());
// LOGW("###########ROPO############");
// }
install(*rt);
tempInstall(*rt);
// env->GetJavaVM(&java_vm);
java_object = env->NewGlobalRef(thiz);
}
在我的应用中,我调用testfn
,基本上是当它到达调用env- esv-&gt; getMethodid的行时,有一个错误,说该方法不是找到,然后该应用迅速崩溃。
04-10 04:04:11.707 22870 22916 F com.example: java_vm_ext.cc:579] JNI DETECTED ERROR IN APPLICATION: JNI NewObjectV called with pending exception java.lang.NoSuchMethodError: no non-static method "Ljava/lang/Class;.getSeed()Ljava/lang/String;"
知道有什么问题吗?
I'm trying to use the JNI in an Android app. My code correctly compiles and the application launches, however when I want to call a Java method (from the calling class) in my C++ code, the app promptly crashes saying it cannot find the method.
You will have to excuse the code, it is a bit messy because I've tried so many things, it is also mixed with React-Native stuff, but the JNI crash has nothing to do with it.
The java module:
package com.jsirnwalletcore;
import androidx.annotation.NonNull;
import androidx.annotation.Keep;
import android.util.Log;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.Base64;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
@Keep
class WalletCoreModule extends ReactContextBaseJavaModule {
public static final String NAME = "WalletCore";
private static native void initialize(long jsiPtr);
private SharedPreferences prefs;
public WalletCoreModule(ReactApplicationContext context) {
super(context);
this.prefs = context.getSharedPreferences("WALLETCORE", Context.MODE_PRIVATE);
}
@NonNull
@Override
public String getName() {
return NAME;
}
public String getSeed() {
return "HARDCODED SEED";
}
@ReactMethod(isBlockingSynchronousMethod = true)
public boolean install() {
try {
String seed = getSeed();
Log.w(NAME, "ROPO CALLING GET SEED: " + seed);
System.loadLibrary("jsi-rn-wallet-core");
ReactApplicationContext context = getReactApplicationContext();
initialize(context.getJavaScriptContextHolder().get());
return true;
} catch(Exception exception) {
return false;
}
}
}
The cpp-adapter.cpp:
#include <jni.h>
#include "installer.hpp"
#include "logs.h"
#include <string>
#include "pthread.h"
#include <sys/types.h>
#include <jsi/jsi.h>
JavaVM *java_vm;
jclass java_class;
jobject java_object;
/**
* A simple callback function that allows us to detach current JNI Environment
* when the thread
* See https://stackoverflow.com/a/30026231 for detailed explanation
*/
void DeferThreadDetach(JNIEnv *env)
{
static pthread_key_t thread_key;
// Set up a Thread Specific Data key, and a callback that
// will be executed when a thread is destroyed.
// This is only done once, across all threads, and the value
// associated with the key for any given thread will initially
// be NULL.
static auto run_once = []
{
const auto err = pthread_key_create(&thread_key, [](void *ts_env)
{
if (ts_env) {
java_vm->DetachCurrentThread();
} });
if (err)
{
// Failed to create TSD key. Throw an exception if you want to.
}
return 0;
}();
// For the callback to actually be executed when a thread exits
// we need to associate a non-NULL value with the key on that thread.
// We can use the JNIEnv* as that value.
const auto ts_env = pthread_getspecific(thread_key);
if (!ts_env)
{
if (pthread_setspecific(thread_key, env))
{
// Failed to set thread-specific value for key. Throw an exception if you want to.
}
}
}
/**
* Get a JNIEnv* valid for this thread, regardless of whether
* we're on a native thread or a Java thread.
* If the calling thread is not currently attached to the JVM
* it will be attached, and then automatically detached when the
* thread is destroyed.
*
* See https://stackoverflow.com/a/30026231 for detailed explanation
*/
JNIEnv *GetJniEnv()
{
JNIEnv *env = nullptr;
// We still call GetEnv first to detect if the thread already
// is attached. This is done to avoid setting up a DetachCurrentThread
// call on a Java thread.
// g_vm is a global.
auto get_env_result = java_vm->GetEnv((void **)&env, JNI_VERSION_1_6);
if (get_env_result == JNI_EDETACHED)
{
if (java_vm->AttachCurrentThread(&env, NULL) == JNI_OK)
{
DeferThreadDetach(env);
}
else
{
// Failed to attach thread. Throw an exception if you want to.
}
}
else if (get_env_result == JNI_EVERSION)
{
// Unsupported JNI version. Throw an exception if you want to.
}
return env;
}
void tempInstall(jsi::Runtime &rt)
{
auto testFn = jsi::Function::createFromHostFunction(
rt,
jsi::PropNameID::forAscii(rt, "testFn"),
0,
[](jsi::Runtime &rt, const jsi::Value &thisValue, const jsi::Value *args, size_t count) -> jsi::Value
{
JNIEnv *jniEnv = GetJniEnv();
java_class = jniEnv->GetObjectClass(java_object);
jmethodID get = jniEnv->GetMethodID(java_class, "getSeed", "()Ljava/lang/String;");
// jobject result = jniEnv->CallObjectMethod(java_object, get);
// const char *str = jniEnv->GetStringUTFChars((jstring)result, NULL);
// LOGW("ROPO GOT: %s", str);
return {};
});
rt.global().setProperty(rt, "testFn", std::move(testFn));
}
extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
{
java_vm = jvm;
LOGW("ROPO JNI_OnLoad CALLED");
return JNI_VERSION_1_6;
}
extern "C" JNIEXPORT void JNICALL
Java_com_jsirnwalletcore_WalletCoreModule_initialize(JNIEnv *env, jobject thiz, jlong jsi)
{
auto rt = reinterpret_cast<jsi::Runtime *>(jsi);
// jclass clazz = env->GetObjectClass(thiz);
// jmethodID getNameMID = env->GetMethodID(clazz, "getName", "()Ljava/lang/String;");
// jstring jClassName = (jstring)env->CallObjectMethod(thiz, getNameMID);
// std::string classString = jstringToString(env, jClassName);
// LOGW("###########ROPO############");
// LOGW("RETRIEVED STRING: %s", classString.c_str());
// LOGW("###########ROPO############");
// jmethodID retrieveMethodID = env->GetMethodID(clazz, "getSeed", "()Ljava/lang/String;");
// if (retrieveMethodID == NULL)
// {
// LOGW("ROPO methodID not found!");
// }
// else
// {
// jstring myString = (jstring)env->CallObjectMethod(thiz, retrieveMethodID);
// std::string retrievedString = jstringToString(env, myString);
// LOGW("###########ROPO############");
// LOGW("RETRIEVED STRING: %s", retrievedString.c_str());
// LOGW("###########ROPO############");
// }
install(*rt);
tempInstall(*rt);
// env->GetJavaVM(&java_vm);
java_object = env->NewGlobalRef(thiz);
}
In my app, I call the testFn
, basically when it reaches the line that calls env->getMethodID, there is an error saying the method is not found and then the app promptly crashes.
04-10 04:04:11.707 22870 22916 F com.example: java_vm_ext.cc:579] JNI DETECTED ERROR IN APPLICATION: JNI NewObjectV called with pending exception java.lang.NoSuchMethodError: no non-static method "Ljava/lang/Class;.getSeed()Ljava/lang/String;"
Any idea what might be wrong?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如 @user7860670在评论中指出的那样,问题在于我将本机方法宣布为静态方法。
尽管它应该是一种实例方法:
即使返回了正确的类名称,它的静态性质也会导致其崩溃。
jni抛出否 real 有用的提示。As pointed out by @user7860670 in the comments, the problem was that I declared the native method as static.
Whereas it should be an instance method:
Even if the correct class name was being returned, the static nature of it caused it to crash.
Big oof that the JNI throws no real helpful hint.