2012-05-29 2 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입니다 내가로드 클래스이며, 해당 인터페이스를 구현합니다. 로딩 코드는 다음과 같습니다 :

디버깅 할 때로드 메소드에서 반환되는 내용을 확인하고 ID 번호가있는 객체인지 확인합니다. 클릭하면 org.jvoicexm[email protected]이 표시되고 드롭 다운에는 JVoiceXmlGrammarProcessor에있는 두 개의 비공개 필드가 표시되어 잘로드 된 것 같습니다. 어떤 아이디어?

+0

grammarProcessor 변수의 클래스는 GrammarProcessor.class입니까? –

+0

새로운'PathClassLoader'를 인스턴스화하는 대신'ClassLoader.getSystemClassLoader()'를 사용하려 했습니까? –

+0

@Marakatu 잠깐, 나는 슈퍼 혼란스러워. 정확히 무엇을하려는 거니? Class 개체를 이름순으로 반환 하시겠습니까? 'Class.forName'을 사용하십시오. 안드로이드에서는 별도의 .dex 파일에서 클래스를로드하려면 새로운 클래스 로더 만 사용해야합니다 ([좋은 기사가 있습니다] (http://android-developers.blogspot.co.uk/2011/07/custom -class-loading-in-dalvik.html)). 당신이하려는 것을 설명하십시오. 편집 : 당신은 같은 apk 또는 다른 하나에서로드 중입니까? – Delyan

답변

12

나는 내가 여기에 무슨 일이 일어나고 있는지 이해 생각하지만 난 org.jvoicexml.android하지 패키지 (현상금이 제안 보인다) 즉, 다른 APK에서로드하는 것을 가정을해야한다.

이를 염두에두고, 이는 불가능하며 정당한 이유가 있습니다.

의 자신의 응용 프로그램과 함께 시작하자 - 당신은 classes.dex 자신의에서와 기본 클래스 로더에 유형을 사용할 수 GrammarProcessor이 (당신이 얻을 PathClassLoader 때 접합자 포크 프로세스). 이 유형을 GP1이라고합시다. GrammarProcessor을 구현하는 애플리케이션의 클래스는 실제로 인터페이스 목록에 GP1을 가지고 있습니다.

그런 다음 새 클래스 로더를 인스턴스화합니다. source을 보면 은 BaseDexClassLoader 주위의 얇은 래퍼이며 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는 먼저 부모와 확인하지 않습니다!

.. 그 짧은 문제입니다.

더 정확하게는 DexPathListDexFile은 다른 클래스의 모든 클래스를로드하고 다른 클래스는 이미 VM에로드 된 클래스를 조사하지 않습니다.

따라서로드 된 두 가지 버전이 GrammarProcessor이됩니다.그런 다음 인스턴스화하려는 객체가 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

나는 그들이 당신이 제공하고있는 PathClassLoader (getSystemClassLoader())를 얻을 90 % 확신은 DexPathList을 얻고 있기 위하여 dexElements 개인 필드를 오버라이드 (override) 추가 과 다른 dex 파일 (귀하의 경우에는 apk)이 필요합니다. Hacky로서 지옥과 나는 그것을 반대 할 것이다.

새로로드 된 클래스를 프레임 워크에서 볼 수있는 방식으로 사용하고 싶지 않다면 BaseDexClassLoader에서 확장하고 적절한 look-in-before-trying- 로드 동작. 나는 그것을하지 않았으므로 그것이 효과가 있다고 약속 할 수는 없다.

내 충고는? 원격 서비스 만 사용하십시오. 이것은 Binder을위한 것입니다. 또는 apk 분리를 다시 생각해보십시오.

+0

ClassLoader의 내부 동작에 대한 정보와 정보가 매우 유익했으며 제안은 실행 가능해 보입니다 (FB 해킹이 아님). 내 상황과 OP와 관련이있을 수도 있습니다. 확장 기능을 판매하려는 응용 프로그램이 있지만 확장 기능은 AIDL이 수행 할 수있는 기능을 넘어서고, 주로 비 Parcelable 개체로 작업해야합니다. 그래서 내가하고 싶은 것은 응용 프로그램의 큰 부분 (예 : 현재로드 된 클래스와의 상호 작용을 제외하고는 대부분 작동하는 완전한 기능을하는 조각)을 가져 오는 것입니다. – JRomero

+0

이 문제는 다음과 같습니다 :'원인 : java.lang.IllegalAccessError : 예기치 않은 구현으로 해결 된 사전 검증 된 클래스의 클래스 참조' – JRomero

+1

아,'dexopt', 이상한 일을하게 될까 봐 걱정되었습니다. 로드 된 덱스의 클래스를 본질적으로 덮어 쓰고 있기 때문에 컴파일러가 사전에 확인한 모든 참조가 유효하지 않게되었습니다. 나보다 더 지식이 많은 사람이 주위를 둘러보기 시작할 수도 있습니다. (전적으로 불가능할 수도 있습니다.) 내 첫 번째 제안은 FB 해킹을 시도하고 다른 dex를 앞에두고 자신의 인터페이스가 아닌 외부 인터페이스 만로드하는 것입니다. – Delyan

관련 문제