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:
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?
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:
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.
Tags und Links android classcastexception dynamic classloader