2012-05-29 4 views
8

私は、システムの特定の実装に応じて必要なリソースに対して異なるクラスを読み込むスレッドを持っています。私の実装はAndroid上にあり、実装に必要な特定のクラスを返すクラスがあります。私は、クラスを正常に読み込むことができるようだが、私は私のメインスレッドでオブジェクトに割り当てようとすると、それは私にClassCastExceptionを与える。ここでは、スニペットです:私のメインスレッドでAndroidでクラスを動的にロードする際のClassCastException

、私は:私は、このスタックトレースできます

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

デバッグ時には、ロードメソッドから戻ってきたものをチェックし、ID番号のオブジェクトです。私がクリックすると[email protected]と表示され、ドロップダウンにはJVoiceXmlGrammarProcessorにある2つのプライベートフィールドが表示されるので、読み込まれているようです。何か案は?

+0

grammarProcessor変数の種類はGrammarProcessor.classですか? –

+0

新しいPathClassLoaderをインスタンス化するのではなく、ClassLoader.getSystemClassLoader()を使用しようとしましたか? –

+0

@Marakatu待って、私はスーパー混乱しています。何を正確にしようとしていますか?クラスオブジェクトを名前で返しますか? 'Class.forName'を使います。 Androidの場合、別の.dexファイルからクラスをロードする場合は、新しいクラスローダーを使用する必要があります([こちらが良い記事](http://android-developers.blogspot.co.uk/2011/07/custom -class-loading-in-dalvik.html))。あなたがしようとしていることを説明してください。編集:あなたは同じapkまたは別のものから読み込んでいますか? – Delyan

答えて

12

私はここで何が起こっているかを理解するが、私は(懸賞金を示唆しているようだとして)org.jvoicexml.androidあなたのパッケージではありません、つまり、あなたは別のAPKからロードしているという仮定をしなければならないと思います。

これを念頭において、これは不可能であり、正当な理由があります。

は、独自のアプリで始まるのをしてみましょう - あなたはあなた自身のclasses.dexから入手し、あなたのデフォルトのクラスローダ(受精卵フォークあなたのプロセスあなたが得るPathClassLoader)にタイプGrammarProcessorを持っています。このタイプはGP1としましょう。 GrammarProcessorを実装しているアプリケーション内のクラスは、実際にはインターフェイスリストにGP1があります。

次に、新しいクラスローダーをインスタンス化します。 sourceを見ると、PathClassLoaderBaseDexClassLoaderの周りの薄いラッパーになり、次ににデリゲートし、DexFileオブジェクトがネイティブコードでロードされます。 Phew。

あなたの悩みの原因ですが、あなたが前にそれを見ていない場合、あなたはそれを逃すかもしれない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は親から最初にチェックしません!

..それはあなたの問題です。

もっと正確には、DexPathListDexFileは、dexのすべてのクラスをロードし、VMにすでにロードされているクラスを調べません。

したがって、GrammarProcessorという2種類の異なるバージョンがロードされてしまいます。次に、インスタンス化しているオブジェクトは、GP1にキャストしようとしている間に、新しいGP2クラスを参照しています。明らかに不可能です。

解決方法はありますか?

これまでに行われていることがありますが、好きではありません。 Facebook use itは、それらの間に強力な関係を持つdexファイルの束を読み込むようにアプリ内にあります。 (すべてのLinearAllocでぐちゃぐちゃ前にそれは、あります):

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

私はDexPathListを取得し、持っているdexElementsプライベートフィールドを上書きし、彼らはあなたが与えられているPathClassLoadergetSystemClassLoader())を取得する90%確信していますその他のdexファイル(あなたの場合はapk)と一緒にを追加してください。地獄のハッキーと私はそれに対して助言するだろう。

新しくロードされたクラスをフレームワークが認識する方法で使用したくない場合は、BaseDexClassLoaderから拡張し、適切なLook-In-Before-Try-負荷の動作に影響を与えます。私はそれをしていないので、私はそれが動作することを約束することはできません。

私の意見は?リモートサービスを使用するだけです。これはBinderのためのものです。また、apkの分離を再考してください。

+0

これは、ClassLoaderの内部動作について非常に有益かつ優れた内部であり、提案は実行可能であると思われます(FBハックではありません)。私の状況は多分OPにも多少関連しています。私は拡張機能を「販売」するアプリケーションを持っていますが、拡張機能はAIDLができることを超えて、主に非Parcelableオブジェクトを扱う必要があります。だから、私がやりたいことは、アプリケーションの大部分(例えば、現在機能しているフラグメントとは、現在ロードされているクラスとのやりとりを除いて動作するようになっている)を持っていくことです。 – JRomero

+0

問題は次のとおりです: '原因:java.lang.IllegalAccessError:予期しないクラス名のクラス参照が予期しない実装に解決されました。 ' – JRomero

+1

ああ、' dexopt'、私は何か変なことをしていました。基本的には、ロードされたdexのクラスを上書きするので、コンパイラによって事前に検証されたすべての参照が無効になります。私よりも知識のある方がそれを回避しようとするだろう(私は完全に不可能かもしれないと思うが)。私の最初の提案は、FBハックを試して、他のdexを前置することです。自分のインターフェイスではなく、外部インターフェイスだけをロードするようにしてください。 – Delyan

関連する問題