2011-07-27 15 views
10

我想調用一些我使用Android NDK從C編寫的Java代碼。該應用程序是一個NativeActivity應用程序。我必須訪問一些僅在Java中可用的功能,並且功能需要您繼承另一個類,所以我不能直接從C中調用。因此,我有這樣的Java代碼:如何在Android上以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); 
    } 
} 

我也有C代碼是這樣的:

// 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); 

} 

隨着在線評論說,運行這個給了我一個「java.lang.NoClassDefFoundError:[通用]」的錯誤。當我解壓apk文件時,它會顯示classes.dex文件,該文件似乎有我的課程。我的猜測是,我對類路徑缺少一些微妙之處,但到目前爲止我無法解決它。

順便說一句,我能夠做出從C標準的Android庫類似的調用沒有問題(在相同的C函數以上)。具體來說,我測試了調用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

以下是我從這個link抽象爲通過Klaimmore提及。

有幾種方法來解決此問題:

做你的findClass查找一次,JNI_OnLoad和高速緩存類引用供以後使用。任何作爲執行JNI_OnLoad一部分的FindClass調用都將使用與調用System.loadLibrary的函數相關聯的類加載器(這是一個特殊規則,用於使庫初始化更加方便)。如果您的應用代碼正在加載庫,FindClass將使用正確的類加載器。*

通過聲明您的本機方法接受Class參數並傳遞Foo,將該類的實例傳遞給需要它的函數。 class in。

緩存對某個地方的ClassLoader對象的引用,並直接發出loadClass調用。這需要一些努力。

12

我的問題的精妙之處,爲什麼由Klaimmore和溫斯頓鏈接到文件並沒有完全解決的問題,從事實,我寫使用NativeActivity的類的應用程序造成的。這意味着從來沒有一個具有本地類加載器的Java堆棧可供我使用。沒有JNI_OnLoad調用,沒有Java方法調用到我的本地函數中,並且沒有其他方法(我知道)獲取本地ClassLoader對象的標記。不幸的是,大多數的Android JNI文檔都不是用NativeActivity編寫的。

但是,這個問題有一個簡單的解決方案,可以使您使用NativeActivity編寫應用程序時更加輕鬆。只需在Java中對NativeActivity進行子類化即可。這允許您從NativeActivity的實例化開始時編寫和訪問任意Java代碼,同時還可以在NativeActivity允許的情況下在C中執行其他所有操作。它還允許您按照這些文檔中描述的方式從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); 
} 

這是我發現將自己的新Java類與NativeActivity應用程序結合在一起。希望這會對除我以外的人有用。

+0

myUsefulJavaFunction是一個jmethodID。 jmethodID不是LocalRef,不應刪除。 – EJP

+0

此替代方法在其他情況下也很重要,例如[this one](http://stackoverflow.com/questions/12894783/linking-shared-fmod-library)! –

+0

這是一個有限的解決方法。如果您有多個自定義類,該怎麼辦?也許可以從Google文檔中提到的JNI_OnLoad中獲取該ClassLoader。 –

相關問題