2011-07-27 5 views
10

मैं कुछ जावा कोड को कॉल करने का प्रयास कर रहा हूं जिसे मैंने एंड्रॉइड एनडीके का उपयोग करके सी से लिखा था। आवेदन एक मूल सक्रियता अनुप्रयोग है। मुझे कुछ कार्यक्षमता तक पहुंचना है जो केवल जावा में उपलब्ध है, और कार्यक्षमता के लिए आपको किसी अन्य वर्ग को उप-वर्ग करने की आवश्यकता है, इसलिए मैं सीधे सी से कॉल नहीं कर सकता। इस प्रकार, मेरे पास जावा कोड है:मैं एंड्रॉइड पर सी में अपनी जावा क्लास कैसे लोड करूं?

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

} 

के रूप में इनलाइन टिप्पणी कहते हैं:

मैं भी इस तरह सी कोड है। जब मैं अपने एपीके को अनपैक करता हूं, तो यह मुझे class.dex फ़ाइल दिखाता है, जिसमें मेरी कक्षा है। मेरा अनुमान है कि कुछ subtlety है कि मैं classpaths के बारे में याद कर रहा हूँ, लेकिन मैं अब तक इसे हल करने में असमर्थ हूं।

संयोग से, मैं किसी समस्या के बिना सी से मानक एंड्रॉइड पुस्तकालयों को समान कॉल करने में सक्षम हूं (उपरोक्त उसी सी फ़ंक्शन में)। विशेष रूप से, मैंने Log.v को कॉल करने का परीक्षण किया और यह आउटपुट को सही ढंग से काम करता है और प्रिंट करता है।

ऐसा लगता है कि मुझे लगता है कि सभी उदाहरण केवल सी से सामान्य जावा पुस्तकालयों को कॉल करने के लिए दिखाते हैं, जावा लाइब्रेरीज़ जिन्हें आपने स्वयं लिखा है, इसलिए मुझे तुलना करने के लिए एक उदाहरण प्रोजेक्ट भी नहीं मिला है।

+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 से मैंने यहां सारण किया है।

JNI_OnLoad में एक बार अपने FindClass लुकअप करते हैं, और श्रेणी संदर्भ कैश बाद में का उपयोग के लिए:

इस के आसपास काम करने के लिए कुछ तरीके हैं। JNI_OnLoad निष्पादित करने के हिस्से के रूप में किए गए किसी भी FindClass कॉल का उपयोग उस सिस्टम से जुड़े वर्ग लोडर का उपयोग करेगा जिसे System.loadLibrary कहा जाता है (यह एक विशेष नियम है, जो लाइब्रेरी प्रारंभिकता को अधिक सुविधाजनक बनाने के लिए प्रदान किया जाता है)। यदि आपका ऐप कोड लाइब्रेरी लोड कर रहा है, तो FindClass सही वर्ग लोडर का उपयोग करेगा। *

क्लास तर्क लेने और फिर Foo को पार करने के लिए अपनी मूल विधि घोषित करके कक्षाओं का एक उदाहरण पास करें। कक्षा

क्लासलोडर ऑब्जेक्ट का संदर्भ कहीं कहीं आसान है, और लोड क्लास कॉल सीधे जारी करें। इसके लिए कुछ प्रयास की आवश्यकता है।

12

मेरे प्रश्न की सूक्ष्मता, और क्यों क्रेम्समोर और विंस्टन द्वारा प्रलेखित दस्तावेज इस मुद्दे को हल नहीं करता है, इस तथ्य से उत्पन्न होता है कि मैं मूल गतिविधि वर्ग का उपयोग करके एक ऐप लिख रहा हूं। इसका मतलब है कि मेरे लिए उपलब्ध स्थानीय क्लास लोडर के साथ कभी भी जावा स्टैक नहीं होता है। कोई जेएनआई_ऑनलोड कॉल नहीं है, मेरे मूल कार्य में कोई जावा विधि नहीं है, और स्थानीय क्लासलोडर ऑब्जेक्ट को पकड़ने के लिए कोई अन्य तरीका नहीं है (जिसे मैं जानता हूं)। दुर्भाग्यवश, अधिकांश एंड्रॉइड जेएनआई दस्तावेज बस मूलभूतता के साथ दिमाग में नहीं लिखा गया है।

हालांकि, इस समस्या का एक सीधा समाधान है जो नेटिव एक्टिविटी का उपयोग करके ऐप्स लिखते समय आपके जीवन को अधिक आसान बना सकता है। जावा में बस मूल सक्रियता subclass। यह आपको अपने मूल सक्रियता के तत्कालता की शुरुआत से मनमाने ढंग से जावा कोड लिखने और एक्सेस करने की अनुमति देता है, जबकि अभी भी सी में अन्य सभी चीजें नेटिव एक्टिविटी की अनुमति देता है। यह आपको उन दस्तावेजों में वर्णित तरीके से सी से अपनी जेएनआई कॉल सेट करने की अनुमति देता है।ऐसा करने से इस तरह निम्नलिखित दिखता है:

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(); 
    } 
} 

और अपने सी पुस्तकालय में:

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; 
} 

फिर सी में कुछ बिंदु पर, आप कर सकते हैं:

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

और वह है जिस तरह से मैंने अपने नए जावा क्लास को नेटिव एक्टिविटी ऐप के साथ शामिल किया। उम्मीद है कि यह मेरे अलावा किसी के लिए उपयोगी होगा।

+0

myUsefulJavaFunction एक jmethodID है। एक jmethodID एक स्थानीय रीफ नहीं है और इसे हटाया नहीं जाना चाहिए। – EJP

+0

यह कामकाज अन्य मामलों में भी महत्वपूर्ण है, जैसे [यह एक] (http://stackoverflow.com/questions/12894783/linking-shared-fmod-library)! –

+0

यह एक सीमित कामकाज है। क्या होगा यदि आपके पास एक से अधिक कस्टम क्लास हैं? शायद Google डॉक्स में उल्लिखित जेएनआई_ऑनलोड से उस क्लासलोडर को प्राप्त करना संभव है। –

संबंधित मुद्दे