0

I want to load some classes from an APK into an already running Service at runtime, but I get the following error:

11-03 16:51:13.570  20227-20240/com.somecompany.android.core W/dalvikvm﹕ Class resolved by unexpected DEX: Lcom/somecompany/android/core/db/DatabaseManager;(0x2ce11280):0x392d6000 ref [Lcom/somecompany/android/core/CoreModule;] Lcom/somecompany/android/core/CoreModule;(0x2cdb1d20):0x38f40000
11-03 16:51:13.570  20227-20240/com.somecompany.android.core W/dalvikvm﹕ (Lcom/somecompany/android/core/db/DatabaseManager; had used a different Lcom/somecompany/android/core/CoreModule; during pre-verification)
11-03 16:51:13.570  20227-20240/com.somecompany.android.core W/dalvikvm﹕ Unable to resolve superclass of Lcom/somecompany/android/core/db/DatabaseManager; (720)
11-03 16:51:13.570  20227-20240/com.somecompany.android.core W/dalvikvm﹕ Link of class 'Lcom/somecompany/android/core/db/DatabaseManager;' failed
11-03 16:51:13.570  20227-20240/com.somecompany.android.core W/dalvikvm﹕ threadid=11: thread exiting with uncaught exception (group=0x2c7dbfc0)
11-03 16:51:13.570  20227-20240/com.somecompany.android.core E/com.somecompany.android.core.CoreApplication﹕ Thread com.somecompany.android.core.CoreService-80000000 has crashed
11-03 16:51:13.570  20227-20240/com.somecompany.android.core E/com.somecompany.android.core.CoreApplication﹕ java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation
            at dalvik.system.DexFile.defineClass(Native Method)
            at dalvik.system.DexFile.loadClassBinaryName(DexFile.java:211)
            at dalvik.system.DexPathList.findClass(DexPathList.java:315)
            at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:58)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
            at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
            at com.somecompany.android.core.CoreModuleLoader.loadRegisterModules(CoreModuleLoader.java:56)
            at com.somecompany.android.core.CoreModuleLoader.load(CoreModuldeLoader.java:110)
            at com.somecompany.android.core.CoreService$1.run(CoreService.java:148)
            at java.lang.Thread.run(Thread.java:856)

I think the problem is that the APK generated by Android automatic build packs all the Service classes into the APK, which at time of loading causes problems because these same classes already exist into the Service. The Service is linked as a JAR at compile time.

How can I modify the build process so the Service JAR is NOT included in resulting APK? Thanks in advance.

EDIT: classes.dex inside the APK does have the Service classes.

EDIT: this is the build.xml node used to DEX the JARs. Unfortunately I have no idea about ANT on how to configure this part, any help appreciated.

<!-- Configurable macro, which allows to pass as parameters output directory,
     output dex filename and external libraries to dex (optional) -->
<macrodef name="dex-helper">
    <element name="external-libs" optional="yes" />
    <attribute name="nolocals" default="false" />
    <sequential>
        <!-- sets the primary input for dex. If a pre-dex task sets it to
             something else this has no effect -->
        <property name="out.dex.input.absolute.dir" value="${out.classes.absolute.dir}" />

        <!-- set the secondary dx input: the project (and library) jar files
             If a pre-dex task sets it to something else this has no effect -->
        <if>
            <condition>
                <isreference refid="out.dex.jar.input.ref" />
            </condition>
            <else>
                <path id="out.dex.jar.input.ref">
                    <path refid="project.all.jars.path" />
                </path>
            </else>
        </if>

        <dex executable="${dx}"
                output="${intermediate.dex.file}"
                dexedlibs="${out.dexed.absolute.dir}"
                nolocals="@{nolocals}"
                forceJumbo="${dex.force.jumbo}"
                disableDexMerger="${dex.disable.merger}"
                verbose="${verbose}">
            <path path="${out.dex.input.absolute.dir}"/>
            <path refid="out.dex.jar.input.ref" />
            <external-libs />
        </dex>
    </sequential>
</macrodef>
m0skit0
  • 25,268
  • 11
  • 79
  • 127
  • This seems to happen to people all the time accidentally... one thing that might work is linking the jar in your java build path but not under the android settings. Another would be a proguard rule to strip it out. – Chris Stratton Nov 03 '14 at 18:35
  • @ChrisStratton The service is in fact linked as JAR in the buildpath. I will check proguard, thanks. – m0skit0 Nov 03 '14 at 21:27
  • @ChrisStratton I think ProGuard [is not the way to go](http://stackoverflow.com/a/12941791/898478). – m0skit0 Nov 04 '14 at 10:59

1 Answers1

1

As explained here, I created a compile-libs/ folder and moved the Service JAR there (removing it from libs/). Then I created a custom_build.xml and included this JAR only for compilation (not for dexing).

<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">

    <path id="java.compiler.classpath.path">
        <fileset dir="compile-libs" includes="*.jar" />
    </path>
    <property name="java.compiler.classpath" refid="java.compiler.classpath.path" />

</project>

This looks like to have solved the problem as the generated DEX does not have the classes from the Service JAR.

EDIT: further testing confirms it is working as expected.

m0skit0
  • 25,268
  • 11
  • 79
  • 127