Android JNI,调用 getMethodID 导致应用程序崩溃

发布于 2025-01-20 06:41:38 字数 7040 浏览 2 评论 0原文

我正在尝试在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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

删除→记忆 2025-01-27 06:41:38

正如 @user7860670在评论中指出的那样,问题在于我将本机方法宣布为静态方法。

private static native void initialize(long jsiPtr);

尽管它应该是一种实例方法:

private native void initialize(long jsiPtr);

即使返回了正确的类名称,它的静态性质也会导致其崩溃。

jni抛出否 real 有用的提示。

As pointed out by @user7860670 in the comments, the problem was that I declared the native method as static.

private static native void initialize(long jsiPtr);

Whereas it should be an instance method:

private native void initialize(long jsiPtr);

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文