2011-07-27 7 views
10

Android NDK를 사용하여 C에서 작성한 Java 코드를 호출하려고합니다. 응용 프로그램은 NativeActivity 응용 프로그램입니다. Java에서만 사용할 수있는 일부 기능에 액세스해야하며 기능을 사용하려면 다른 클래스를 서브 클래스해야하므로 C에서 직접 호출 할 수는 없습니다. 따라서 다음과 같은 Java 코드가 있습니다.안드로이드에서 C로 Java 클래스를로드하려면 어떻게해야합니까?

// src/com/example/my/package/SubClass.java 
package com.example.my.package; 

import android.foo.TheSuperClass; 

public class SubClass extends TheSuperClass { 
    public SubClass() { 
    setImportantProperty(true); 
    } 
} 
오류 :이 나에게 "[일반] java.lang.NoClassDefFoundError가"를 제공 실행, 인라인 주석이 말한대로

// Some file.c 
void setThatImportantJavaProperty() { 
    JavaVM *vm = AndroidGetJavaVM(); // This returns a valid JavaVM object 
    JNIEnv* env; 
    (*vm)->AttachCurrentThread(vm, &env, 0); 

    jclass theSubClass = (*env)->FindClass(env, "com/example/my/package/SubClass"); 
    jthrowable exception = (*env)->ExceptionOccurred(env); 
    if (exception) { 
    (*env)->ExceptionDescribe(env); 
    // This gives me: "java.lang.NoClassDefFoundError: [generic]". 
    // Also, theSubClass is null, so the next line causes a segfault. 
    } 
    jmethodID theSubClassConstructor = (*env)->GetMethodID(env, theSubClass, "<init>", "()V"); 
    jobject theSubClassObject = (*env)->NewObject(env, theSubClass, theSubClassConstructor); 

    (*env)->DeleteLocalRef(env, theSubClass); 
    (*env)->DeleteLocalRef(env, theSubClassConstructor); 
    (*env)->DeleteLocalRef(env, theSubClassObject); 

    (*vm)->DetachCurrentThread(vm); 

} 

:

나는이 같은 C 코드가 있습니다. apk 패키지를 풀면 class.dex 파일이 표시됩니다. 클래스 파일에 클래스가있는 것 같습니다. 내 생각 엔 클래스 패스와 관련하여 누락 된 미묘한 점이 있지만 지금까지 해결할 수 없다.

덧붙여 말하자면 위의 동일한 C 함수에서 문제없이 C에서 표준 Android 라이브러리에 비슷한 호출을 할 수 있습니다. 특히, 나는 Log.v를 호출하는 것을 테스트했으며, 그 함수는 올바르게 작동하고 결과를 정확하게 출력한다.

내가 찾는 모든 예제는 Java 라이브러리가 아닌 C에서 일반적인 Java 라이브러리를 호출하는 방법을 보여 주므로 비교할 예제 프로젝트조차 찾지 못했습니다.

+3

이 내용을 읽었습니까? http://developer.android.com/guide/practices/design/jni.html#faq_FindClass – Klaimmore

+0

예, 했었습니다. 감사합니다 - 그것은 올바른 방향으로 향하고 있습니다. 일단 완료하면 완전한 해결책을 게시 할 것입니다. – itfische

+0

http://groups.google.com/group/android-developers/browse_thread/thread/e090b94fe958ab31 – Wei

답변

2

다음은 Klaimmore가 언급 한대로 link에서 추상화 한 것입니다.

이주는 JNI_OnLoad에, 한 번에 FindClass 조회를 수행하고, 나중에를 사용하기위한 클래스 참조를 캐시 :

는이 문제를 해결하기위한 몇 가지 방법이 있습니다. JNI_OnLoad 실행의 일부로 작성된 FindClass 호출은 System.loadLibrary를 호출 한 함수와 연관된 클래스 로더를 사용합니다 (라이브러리 초기화를보다 편리하게하기 위해 제공되는 특별한 규칙). 앱 코드가 라이브러리를로드하는 경우 FindClass는 올바른 클래스 로더를 사용합니다. *

네이티브 메소드가 Class 인수를 취하고 Foo를 전달하도록 선언하여 클래스의 인스턴스를 필요한 함수에 전달합니다.

ClassLoader 객체에 대한 참조를 어딘가에 편리하게 캐시하고 loadClass 호출을 직접 실행하십시오. 이것은 약간의 노력이 필요합니다.

12

내 질문의 미묘함과 Klaimmore와 Winston이 링크 한 문서가이 문제를 해결하지 않는 이유는 NativeActivity 클래스를 사용하여 앱을 작성하고 있기 때문입니다. 즉, 나에게 사용할 수있는 로컬 클래스 로더가있는 Java 스택이 없다는 의미입니다. JNI_OnLoad 호출이 없으며 Java 메소드가 내 원시 함수로 호출되지 않으며 로컬 ClassLoader 객체를 가져 오는 다른 방법도 없습니다. 안타깝게도 대부분의 Android JNI 문서는 NativeActivity를 염두에두고 작성된 것이 아닙니다.

그러나 NativeActivity를 사용하여 앱을 작성하면 훨씬 쉽게이 문제를 해결할 수 있습니다. Java에서 NativeActivity를 서브 클래스하기 만하면됩니다. 이를 통해 NativeActivity가 허용하는대로 C의 다른 모든 작업을 수행하면서 NativeActivity의 인스턴스화 초기부터 임의의 Java 코드를 작성하고 액세스 할 수 있습니다. 또한이 문서에 설명 된 방식으로 C에서 JNI 호출을 설정할 수 있습니다.

package com.example.my.package; 

import android.app.NativeActivity; 
import android.util.Log; 

public class MyNativeActivity extends NativeActivity { 
    static { 
    System.loadLibrary("my_ndk_lib"); 
    } 

    private static String TAG = "MyNativeActivity"; 

    public MyNativeActivity() { 
    super(); 
    Log.v(TAG, "Creating MyNativeActivity"); 
    } 

    public static void MyUsefulJavaFunction() { 
    doSomethingAwesome(); 
    } 
} 

그리고 당신의 C 라이브러리에

가 :
jint JNI_OnLoad(JavaVM* vm, void* reserved) 
{ 
    JNIEnv* env; 
    if ((*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK) 
    return -1; 

    globalMyNativeActivityClass = (*env)->NewGlobalRef(env, (*env)->FindClass(env, "com/example/my/package/MyNativeActivity")); 

    return JNI_VERSION_1_6; 
} 

그런 다음 C의 어느 시점에서, 당신이 할 수 있습니다

// Some file.c 
void doSomethingAwesomeInJava() { 
    JavaVM *vm = AndroidGetJavaVM(); // This returns a valid JavaVM object 
    JNIEnv* env; 
    (*vm)->AttachCurrentThread(vm, &env, 0); 

    jmethodID myUsefulJavaFunction = (*env)->GetStaticMethodID(env, globalMyNativeActivityClass, "MyUsefulJavaFunction", "()V"); 
    (*env)->CallStaticVoidMethod(env, theActivityClass, myUsefulJavaFunction); 

    (*env)->DeleteLocalRef(env, myUsefulJavaFunction); 

    (*vm)->DetachCurrentThread(vm); 
} 

을 그리고 그이다이 다음이 같이 보입니다 수행 방법은 NativeActivity 응용 프로그램으로 내 자신의 새 Java 클래스를 통합하는 것으로 나타났습니다. 바라기를 이것은 나 외에 누군가에게 유용 할 것입니다.

+0

myUsefulJavaFunction은 jmethodID입니다. jmethodID는 LocalRef가 아니기 때문에 삭제하면 안됩니다. – EJP

+0

이 해결 방법은 [this one] (http://stackoverflow.com/questions/12894783/linking-shared-fmod-library)과 같은 다른 경우에도 중요합니다. –

+0

제한된 해결 방법입니다. 두 개 이상의 사용자 정의 클래스가 있으면 어떻게됩니까? 어쩌면 그 JNI_OnLoad에서 그 ClassLoader 얻을 수있는 Google 워드 프로세서 언급했다. –

관련 문제