ClassCastException beim dynamischen Laden einer Klasse in Android

8

Ich habe einen Thread, der abhängig von der spezifischen Implementierung des Systems verschiedene Klassen für die benötigten Ressourcen lädt. Meine Implementierung ist auf Android und ich habe eine Klasse, die die spezifischen Klassen zurückgibt, die von meiner Implementierung benötigt werden. Ich scheine in der Lage zu sein, die Klasse in Ordnung zu laden, aber wenn ich versuche, sie dem Objekt in meinem Haupt-Thread zuzuordnen, bekomme ich eine ClassCastException. Hier sind die Schnipsel:

In meinem Hauptthread mache ich:

%Vor%

was mir diesen Stacktrace gibt:

%Vor%

GrammarProcessor ist eine Schnittstelle und JVoiceXmlGrammarProcessor ist die Klasse, die ich lade und diese Schnittstelle implementiert. Der Ladecode lautet wie folgt:

%Vor%

Beim Debugging prüfe ich, was von der load-Methode zurückgegeben wird, und es ist ein Objekt mit einer ID-Nummer. Wenn ich darauf klicke, wird org.jvoicexml.android.JVoiceXmlGrammarProcessor@40565820 angezeigt, und in der Dropdown-Liste werden die zwei privaten Felder angezeigt, die ein JVoiceXmlGrammarProcessor haben sollte. Es scheint also gut geladen zu sein. Irgendwelche Ideen?

    
Marakatu 29.05.2012, 01:29
quelle

1 Antwort

12

Ich denke, ich verstehe, was hier passiert, aber ich muss annehmen, dass org.jvoicexml.android nicht dein Paket ist, dh du lädst aus einer anderen apk (wie es das Kopfgeld vermuten lässt) ).

In diesem Sinne ist dies unmöglich und aus einem guten Grund.

Beginnen wir mit Ihrer eigenen App - Sie haben den Typ GrammarProcessor von Ihrer eigenen classes.dex und in Ihrem Standard ClassLoader (die PathClassLoader , die Sie erhalten, wenn die Zygote Ihren Prozess abbricht). Nennen wir diesen Typ GP1 . Jede Klasse in Ihrer eigenen Anwendung, die GrammarProcessor implementiert, hat tatsächlich GP1 in ihrer Schnittstellenliste.

Dann instanziieren Sie einen neuen Klassenlader. Wenn Sie sich die Quelle , sehen Sie, dass PathClassLoader nur ein dünner Wrapper um BaseDexClassLoader , das wiederum an DexPathList , die wiederum an DexFile Objekte, die wiederum das Laden übernehmen nativen Code. Puh.

Es gibt einen subtilen Teil von BaseDexClassLoader , der die Ursache für Ihre Probleme ist, aber wenn Sie es vorher noch nicht gesehen haben, könnten Sie es vermissen:

%Vor%

und ein bisschen weiter unten:

%Vor%

BaseDexClassLoader prüft nicht zuerst mit seinem übergeordneten Element!

.. und das ist kurz Ihr Problem.

Genauer gesagt laden die DexPathList und DexFile darin alle Klassen aus der anderen dex und schauen niemals in die Klassen, die bereits in der VM geladen sind.

Sie haben also zwei verschiedene geladene Versionen von GrammarProcessor . Das Objekt, das Sie instanziieren, bezieht sich dann auf die neue Klasse GP2 , während Sie versuchen, es in GP1 zu konvertieren. Offensichtlich unmöglich.

Gibt es dafür eine Lösung?

Es gibt einen, der schon einmal gemacht wurde, aber Sie werden es nicht mögen. Facebook benutze es in ihre App, um eine Reihe von dex -Dateien mit starken Beziehungen zwischen ihnen zu laden. (Es ist da, vor all dem Herumspielen mit LinearAlloc ):

  

Wir haben den Android-Quellcode untersucht und mithilfe von Java-Reflektion einige seiner internen Strukturen direkt geändert

Ich bin zu 90% sicher, dass sie das angegebene PathClassLoader erhalten ( getSystemClassLoader() ), das DexPathList erhalten und das dexElements private Feld überschreiben, um ein zusätzliches Element mit dem anderen Dex zu haben Datei (apk in Ihrem Fall). Hacky wie die Hölle und ich würde davon abraten.

Es ist mir nur in den Sinn gekommen, dass Sie, wenn Sie die neu geladenen Klassen nicht so verwenden möchten, dass das Framework sie sieht, von BaseDexClassLoader aus erweitern und das richtige Look-In-Parent-Before-Trying implementieren könnten -zu-laden-Verhalten. Ich habe es nicht getan, also kann ich nicht versprechen, dass es funktionieren wird.

Mein Rat? Verwenden Sie nur Remote-Dienste. Dafür ist Binder gedacht. Alternativ, überdenken Sie Ihre apk Trennung.

    
Delyan 27.07.2013 23:38
quelle