2012-05-29 29 views
8

में गतिशील रूप से एक वर्ग लोड करते समय मेरे पास एक थ्रेड है जो सिस्टम के विशिष्ट कार्यान्वयन के आधार पर आवश्यक संसाधनों के लिए विभिन्न वर्गों को लोड करता है। मेरा कार्यान्वयन एंड्रॉइड पर है और मेरे पास एक कक्षा है जो मेरे कार्यान्वयन द्वारा आवश्यक विशिष्ट वर्गों को लौटाती है। मैं कक्षा को ठीक से लोड करने में सक्षम हूं, लेकिन जब मैं इसे अपने मुख्य धागे में ऑब्जेक्ट को असाइन करने का प्रयास करता हूं, तो यह मुझे क्लासकास्ट अपवाद देता है। यहाँ के टुकड़े कर रहे हैं:क्लासकास्ट अपवाद जबकि एंड्रॉइड

मेरी मुख्य थ्रेड में, मैं कार्य करें:

try { 
     grammarProcessor = config.loadObject(GrammarProcessor.class); 

जो मुझे इस स्टैकट्रेस देता है:

E/AndroidRuntime(6682): FATAL EXCEPTION: JVoiceXmlMain 
    E/AndroidRuntime(6682): java.lang.ClassCastException: org.jvoicexml.android.JVoiceXmlGrammarProcessor 
    E/AndroidRuntime(6682):  at org.jvoicexml.JVoiceXmlMain.run(JVoiceXmlMain.java:321) 

GrammarProcessor एक अंतरफलक और JVoiceXmlGrammarProcessor है वर्ग है कि मैं लोड है और इंटरफ़ेस लागू करता है। लोड हो रहा है कोड इस प्रकार है:

else if(baseClass == GrammarProcessor.class){ 
     String packageName = "org.jvoicexml.android"; 
     String className = "org.jvoicexml.android.JVoiceXmlGrammarProcessor";   
     String apkName = null; 
     Class<?> handler = null; 
     T b = null; 

     try { 
      PackageManager manager = callManagerContext.getPackageManager(); 
      ApplicationInfo info= manager.getApplicationInfo(packageName, 0); 
      apkName= info.sourceDir; 
     } catch (NameNotFoundException e1) { 
      // TODO Auto-generated catch block 
      e1.printStackTrace(); 
      return null; 
     } 
     PathClassLoader myClassLoader = 
      new dalvik.system.PathClassLoader(
        apkName, 
        ClassLoader.getSystemClassLoader()); 
     try { 
      handler = Class.forName(className, true, myClassLoader); 
      return (T) handler.newInstance(); 
     } catch (ClassNotFoundException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
      return null; 
     }   
     catch (InstantiationException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
      return null; 
     } catch (IllegalAccessException e) { 
      // TODO Auto-generated catch block 
      e.printStackTrace(); 
      return null; 
     } 
} 

जब डीबगिंग, मैं जाँच क्या लोड विधि से लौट रहे है और यह एक आईडी नंबर के साथ एक वस्तु है। यदि मैं उस पर क्लिक करता हूं, तो यह [email protected] कहेंगे, और ड्रॉपडाउन दो निजी फ़ील्ड दिखाएगा जो JVoiceXmlGrammarProcessor होना चाहिए, इसलिए ऐसा लगता है कि यह अच्छी तरह से लोड हो गया है। कोई विचार?

+0

व्याकरणप्रोसेसर परिवर्तक कक्षा व्याकरणप्रोसेसर.क्लास का है ?? –

+0

क्या आपने नए 'PathClassLoader' को तुरंत चालू करने के बजाय 'ClassLoader.getSystemClassLoader()' का उपयोग करने का प्रयास किया था? –

+0

@ माराकातु रुको, मैं बहुत उलझन में हूं। आप वास्तव में क्या करने की कोशिश कर रहे हैं? नाम से कक्षा ऑब्जेक्ट वापस करें? 'Class.forName' का प्रयोग करें। एंड्रॉइड पर, यदि आप अलग-अलग .dex फ़ाइल से कक्षाएं लोड करना चाहते हैं तो आपको केवल नए क्लासलोडर का उपयोग करने की आवश्यकता है ([यहां एक अच्छा लेख] (http://android-developers.blogspot.co.uk/2011/07/custom श्रेणी लोडिंग-इन-dalvik.html))। कृपया बताएं कि आप क्या करने की कोशिश कर रहे हैं। संपादित करें: क्या आप एक ही एपीके या एक अलग से लोड हो रहे हैं? – Delyan

उत्तर

12

मुझे लगता है कि मैं समझता हूँ कि यहाँ क्या हो रहा है, लेकिन मैं एक धारणा बनाने के लिए है कि org.jvoicexml.androidनहीं अपने पैकेज, जैसे कि, आप एक अलग apk से लोड कर रहे हैं (के रूप में इनाम का सुझाव करने लगता है) है।

इस बात को ध्यान में रखते हुए, यह असंभव है और एक अच्छे कारण के लिए।

के अपने स्वयं के अनुप्रयोग के साथ शुरू करते हैं - आप अपने खुद के classes.dex से और अपने डिफ़ॉल्ट classloader में प्रकार GrammarProcessor उपलब्ध (PathClassLoader है कि आप जब युग्मनज कांटे अपनी प्रक्रिया)। आइए इस प्रकार को GP1 पर कॉल करें। GrammarProcessor लागू करने वाले आपके स्वयं के एप्लिकेशन में कोई भी वर्ग वास्तव में GP1 उनकी इंटरफ़ेस सूची में है।

फिर, आप एक नए क्लासलोडर को तुरंत चालू करते हैं। यदि आप source देखते हैं, तो आप देखेंगे कि PathClassLoaderBaseDexClassLoader के आस-पास एक पतली आवरण है जो बदले में DexPathList पर प्रतिनिधि करता है, जो बदले में DexFile ऑब्जेक्ट्स को प्रतिनिधि करता है जो बदले में मूल कोड में लोडिंग करता है। ओह।

वहाँ अपनी परेशानियों का कारण है कि BaseDexClassLoader का एक सूक्ष्म हिस्सा है, लेकिन अगर आप यह पहले नहीं देखा है, तो आप यह याद आती है हो सकता है:

this.pathList = new DexPathList(this, dexPath, libraryPath, optimizedDirectory); 

और थोड़ा और नीचे:

@Override 
protected Class<?> findClass(String name) throws ClassNotFoundException { 
    Class c = pathList.findClass(name); 
    if (c == null) { 
     ... 
    } 
    return c; 
} 

BaseDexClassLoader पहले अपने माता-पिता से जांच नहीं करता है!

.. और संक्षेप में आपकी समस्या है।

अधिक सटीक, DexPathList और DexFile इसके अंदर अन्य dex से सभी कक्षाएं लोड करें और वीएम में पहले से लोड की गई कक्षाओं को कभी भी न देखें।

तो, आप GrammarProcessor के दो अलग-अलग लोड संस्करणों के साथ समाप्त होते हैं।फिर, जिस ऑब्जेक्ट को आप तत्काल कर रहे हैं वह नए GP2 वर्ग का जिक्र कर रहा है, जबकि आप इसे GP1 पर डालने का प्रयास कर रहे हैं। जाहिर है असंभव।

क्या इसका कोई समाधान है?

ऐसा कुछ है जो पहले किया गया है, लेकिन आपको यह पसंद नहीं आएगा। फेसबुक use it उनके ऐप में dex फ़ाइलों का एक गुच्छा लोड करने के लिए उनके बीच मजबूत संबंधों के साथ। (यह वहाँ, LinearAlloc साथ के बारे में है सब खिलवाड़ करने से पहले):

we examined the Android source code and used Java reflection to directly modify some of its internal structures

मैं 90% यकीन है कि वे PathClassLoader है कि आप दिया जाता है (getSystemClassLoader()) मिलता हूँ, DexPathList हो और dexElements निजी क्षेत्र को ओवरराइड करने के लिए अन्य डीएक्स फ़ाइल (आपके मामले में एपीके) के साथ एक अतिरिक्त Element। नरक के रूप में हैकी और मैं इसके खिलाफ सलाह देंगे।

यह सिर्फ मेरे लिए हुआ कि अगर आप नए लोड किए गए वर्गों का उपयोग इस तरह से नहीं करना चाहते हैं कि फ्रेमवर्क उन्हें देखे, तो आप BaseDexClassLoader से बढ़ा सकते हैं और उचित दिखने वाले माता-पिता को पहले-कोशिश कर सकते हैं- टू-लोड व्यवहार। मैंने यह नहीं किया है, इसलिए मैं वादा नहीं कर सकता कि यह काम करेगा।

मेरी सलाह? बस दूरस्थ सेवाओं का उपयोग करें। यह Binder के लिए है। वैकल्पिक रूप से, अपने एपीके अलगाव पर पुनर्विचार करें।

+0

वह बेहद जानकारीपूर्ण और क्लासलोडर के आंतरिक कार्यकलापों में अच्छा अंतर्दृष्टि था और सुझाव व्यवहार्य प्रतीत होता है (एफबी हैक नहीं)। मेरी स्थिति, और हो सकता है कि ओपी से कुछ हद तक संबंधित हो, यह भी है कि मेरे पास एक ऐसा एप्लिकेशन है जिसे मैं एक्सटेंशन "बेचना" चाहता हूं लेकिन एक्सटेंशन को एडीएल करने में सक्षम होने की आवश्यकता है, मुख्य रूप से गैर पार्सेलबल ऑब्जेक्ट्स के साथ काम करना। तो मैं जो करना चाहता हूं वह एप्लिकेशन के बड़े हिस्सों को लाता है (जैसे कि पूर्ण कार्यरत टुकड़े, जो कि मेरे पास वर्तमान भारित वर्गों के साथ बातचीत के अपवाद के साथ काम करने के लिए सबसे अधिक भाग के लिए प्राप्त है)। – JRomero

+0

अब मुद्दा यह है: 'कारण: java.lang.IllegalAccessError: पूर्व-सत्यापित कक्षा में कक्षा रेफरी अप्रत्याशित कार्यान्वयन के लिए हल किया गया – JRomero

+1

आह, 'dexopt', मुझे डर था कि यह कुछ अजीब करने जा रहा था। चूंकि आप लोड किए गए डेक्स में कक्षा को अनिवार्य रूप से ओवरराइट कर रहे हैं, इसलिए संकलक द्वारा पूर्व-सत्यापित किए गए सभी संदर्भ अमान्य हो गए हैं। यह मेरे आस-पास होने शुरू करने के लिए किसी से ज्यादा जानकार होगा (हालांकि मुझे डर है कि यह पूरी तरह असंभव हो सकता है)। मेरा पहला सुझाव है कि एफबी हैक को आजमाएं और अन्य डीएक्स को प्रीपेड करें, यह सुनिश्चित कर लें कि केवल विदेशी इंटरफ़ेस को लोड करें, न कि स्वयं। – Delyan