47

AndroidのJNIヒントページにこのFAQが記載されています:FindClassが私のクラスを見つけられなかったのはなぜですか? 彼らは複数の解決策に言及しており、最後の選択肢はこれです:

便利な場所にClassLoaderオブジェクトへの参照をキャッシュし、loadClass呼び出しを直接発行します。これには多少の努力が必要です。

それで、私はそれを機能させようとしました、そして、何があっても、この方法は単に私のために機能しないようです。最終的に、ClassLoaderの使用方法を理解しましたが、ネイティブスレッドから、まだタッチ/ロードされていないloadClassを実行しようとすると、機能しません。基本的に、ネイティブスレッドから呼び出されたときの動作はenv-> FindClassと同じですが、アプリですでに使用されているクラスに対して0が返されない点が異なります。私がそれを正しく理解しなかった場合、またはまだ使用/ロードされていないネイティブスレッドからクラスにアクセスすることが不可能であるかどうかはわかりません。






編集:私が正確に何を意味するのかを説明するために、より多くの情報を提供します。通常のJNIと、cachedを使用env->FindClass(className)する私が書いた別のJNIがあります。myFindClass(env, className)ClassLoader->loadClass

ネイティブc/c++からアクセスしようとしているクラスは「com/noname/TestClient」です。myFindClass内では、env->FindClassとそれが返すログ値も使用します。

jclass myFindClass(JNIEnv * env, const char* name)
{
    ...
    jclass c0 = env->FindClass(name);
    jclass c1 = (jclass)env->CallObjectMethod(ClassLoader,
        MID_loadClass, envNewStringUTF(name));
    dlog("myFindClass(\"%s\") => c0:%p, c1:%p, c0 and c1 are same: %d",
        name, c0, c1, env->IsSameObject(c0, c1));
    ...
}

次に、問題を説明するためにこれらの3つの組み合わせがあります。

1)

//inside JNI_OnLoad thread
myFindClass(env, "com/noname/TestClient");
...

//inside native thread created by pthread_create
myFindClass(env, "com/noname/TestClient");

私はこのlogcatを取得します:

myFindClass( "com / noname / TestClent")=> c0:0x41b64558、c1:0x41b64558、c0とc1は同じです:1
...
myFindClass( "com / noname / TestClent")=> c0:0、c1:0x41b64558、 c0とc1は同じです:0

2)

//inside JNI_OnLoad thread
env->FindClass("com/noname/TestClient");
...

//inside native thread created by pthread_create
myFindClass("com/noname/TestClient");

私はこのlogcatを取得します:

myFindClass( "com / noname / TestClent")=> c0:0、c1:0x41b64558、c0とc1は同じです:0

3)

//inside JNI_OnLoad thread
//"com/noname/TestClient" isn't touched from JNI_OnLoad.
...

//inside native thread created by pthread_create
myFindClass(env, "com/noname/TestClient");

私はこのlogcatを取得します:

myFindClass( "com / noname / TestClent")=> c0:0、c1:0、c0、c1は同じです:1

基本的に、私の問題は、ClassLoaderが3番目のケースで私のクラスを見つけられないことです。バグですか?問題を解決するために何ができるでしょうか?

EDIT2: それに加えて、ClassLoader::loadClassは明らかにバグがあるようです。myFindClass( "noname / TestClent")に問い合わせると、ガベージが返され、返されたjclassを使用すると、アプリがクラッシュします。

4

2 に答える 2

73

アプリを何度も試してクラッシュさせた後、同僚と私は別のネイティブ スレッドでクラス ローダーをキャッシュし、正常に使用することができました。使用したコードを以下に示します (C++11 ですが、C++2003 に簡単に変換できます)。前述の「ClassLoader オブジェクトへの参照を便利な場所にキャッシュし、loadClass を発行する」の例が見つからなかったため、ここに投稿します。直接呼び出します。これには多少の労力が必要です。". JNI_OnLoad のスレッドとは異なるスレッドから呼び出された場合、findClass の呼び出しは完全に機能しました。これが役立つことを願っています。

JavaVM* gJvm = nullptr;
static jobject gClassLoader;
static jmethodID gFindClassMethod;

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *pjvm, void *reserved) {
    gJvm = pjvm;  // cache the JavaVM pointer
    auto env = getEnv();
    //replace with one of your classes in the line below
    auto randomClass = env->FindClass("com/example/RandomClass");
    jclass classClass = env->GetObjectClass(randomClass);
    auto classLoaderClass = env->FindClass("java/lang/ClassLoader");
    auto getClassLoaderMethod = env->GetMethodID(classClass, "getClassLoader",
                                             "()Ljava/lang/ClassLoader;");
    gClassLoader = env->CallObjectMethod(randomClass, getClassLoaderMethod);
    gFindClassMethod = env->GetMethodID(classLoaderClass, "findClass",
                                    "(Ljava/lang/String;)Ljava/lang/Class;");

    return JNI_VERSION_1_6;
}

jclass findClass(const char* name) {
    return static_cast<jclass>(getEnv()->CallObjectMethod(gClassLoader, gFindClassMethod, getEnv()->NewStringUTF(name)));
}

JNIEnv* getEnv() {
    JNIEnv *env;
    int status = gJvm->GetEnv((void**)&env, JNI_VERSION_1_6);
    if(status < 0) {    
        status = gJvm->AttachCurrentThread(&env, NULL);
        if(status < 0) {        
            return nullptr;
        }
    }
    return env;
}
于 2013-04-30T14:47:42.143 に答える
6

まず、ネイティブ スレッドを JVM に接続してみてください。

最初に取得できるjvmへのポインタJNI_OnLoad

env->GetJavaVM(&jvm);

次に、ネイティブスレッドから

JNIEnv *env;
jvm->AttachCurrentThread((void **)&env, NULL);

次に、それを使用しenvますFindClass

于 2012-11-07T06:20:42.090 に答える