Android Dependency Double Trouble

I’ve been learning some Android programming and decided to start out with the “New Build System“. I arrived at a point where it feels right to start dividing code up into separate modules. AndroidStudio and Gradle make it very easy to do so, but I hit a bump with a dex.

I quickly built a structure like the above diagram and wired up the dependencies between the modules as follows:

In AndroidApp/build.gradle:

dependencies {
    compile project(':Core')
    instrumentTestCompile project(':TestSupport')
}

In TestSupport/build.gradle:

dependencies {
    compile project(':Core')
}

The app builds and runs fine, but there are problems when running the tests.

$ ./gradlew clean connectedCheck
...snip...
Test failed to run to completion. Reason: 'Instrumentation run failed due to 'java.lang.IllegalAccessError''. Check device logcat for details

Logcat had lots to say (full details at the end), but the most interesting parts were:

12-02 15:36:03.750  19161-19161/in.sinking.project W/dalvikvm? Class resolved by unexpected DEX: Lin/sinking/project/ImplementationOfAnInterfaceFromCore;(0x42d850e0):0x420bb000 ref [Lin/sinking/project/core/AnInterface;] Lin/sinking/project/core/AnInterface;(0x42d850e0):0x5a5cb000
12-02 15:36:03.750  19161-19161/in.sinking.project W/dalvikvm? (Lin/sinking/project/ImplementationOfAnInterfaceFromCore; had used a different Lin/sinking/project/core/AnInterface; during pre-verification)
12-02 15:36:03.750  19161-19161/in.sinking.project I/dalvikvm? Failed resolving Lin/sinking/project/ImplementationOfAnInterfaceFromCore; interface 38 'Lin/sinking/project/core/AnInterface;'
...snip...
     Caused by: java.lang.IllegalAccessError: Class ref in pre-verified class resolved to unexpected implementation

Searching the web for “Class ref in pre-verified class resolved to unexpected implementation” reveals this problem happens when the app and test packages (.apk) each include a copy of a class (see here, here, here and here). In this case, the classes from Core are being included once in each package. I don’t know enough about the details of linking on Android and how the test and app packages interact, but the fix is to ensure that the Core classes are not included as a compile dependency of the TestSupport package.

So TestSupport’s dependency on Core is what I think maven-types would call “provided”. Doing the same in Gradle is simple when you have How do I best define dependencies as “provided”? to tell you how. TestSupport/build.gradle has to expand to become:

configurations {
    provided
}
 
sourceSets {
    main {
        compileClasspath += configurations.provided
    }
}
 
dependencies {
    provided project(':Core')
}

Edit (Dec 4, 2013)

NB: this implementation of provided is incomplete. See Provided Scope in Gradle for more info.

Full logcat entry

I’ve included the full logcat entry here, just for reference.

12-02 15:36:03.750  19161-19161/in.sinking.project W/dalvikvm? Class resolved by unexpected DEX: Lin/sinking/project/ImplementationOfAnInterfaceFromCore;(0x42d850e0):0x420bb000 ref [Lin/sinking/project/core/AnInterface;] Lin/sinking/project/core/AnInterface;(0x42d850e0):0x5a5cb000
12-02 15:36:03.750  19161-19161/in.sinking.project W/dalvikvm? (Lin/sinking/project/ImplementationOfAnInterfaceFromCore; had used a different Lin/sinking/project/core/AnInterface; during pre-verification)
12-02 15:36:03.750  19161-19161/in.sinking.project I/dalvikvm? Failed resolving Lin/sinking/project/ImplementationOfAnInterfaceFromCore; interface 38 'Lin/sinking/project/core/AnInterface;'
12-02 15:36:03.750  19161-19161/in.sinking.project W/dalvikvm? Link of class 'Lin/sinking/project/ImplementationOfAnInterfaceFromCore;' failed
12-02 15:36:03.775  19161-19161/in.sinking.project W/ClassPathPackageInfoSource? Cannot load class. Make sure it is in your apk. Class name: 'in.sinking.project.ImplementationOfAnInterfaceFromCore'. Message: in.sinking.project.ImplementationOfAnInterfaceFromCore
    java.lang.ClassNotFoundException: in.sinking.project.ImplementationOfAnInterfaceFromCore
            at java.lang.Class.classForName(Native Method)
            at java.lang.Class.forName(Class.java:217)
            at android.test.ClassPathPackageInfoSource.createPackageInfo(ClassPathPackageInfoSource.java:88)
            at android.test.ClassPathPackageInfoSource.access$000(ClassPathPackageInfoSource.java:39)
            at android.test.ClassPathPackageInfoSource$1.load(ClassPathPackageInfoSource.java:50)
            at android.test.ClassPathPackageInfoSource$1.load(ClassPathPackageInfoSource.java:47)
            at android.test.SimpleCache.get(SimpleCache.java:31)
            at android.test.ClassPathPackageInfoSource.getPackageInfo(ClassPathPackageInfoSource.java:72)
            at android.test.ClassPathPackageInfo.getSubpackages(ClassPathPackageInfo.java:48)
            at android.test.ClassPathPackageInfo.addTopLevelClassesTo(ClassPathPackageInfo.java:61)
            at android.test.ClassPathPackageInfo.getTopLevelClassesRecursive(ClassPathPackageInfo.java:55)
            at android.test.suitebuilder.TestGrouping.testCaseClassesInPackage(TestGrouping.java:156)
            at android.test.suitebuilder.TestGrouping.addPackagesRecursive(TestGrouping.java:117)
            at android.test.suitebuilder.TestSuiteBuilder.includePackages(TestSuiteBuilder.java:102)
            at android.test.InstrumentationTestRunner.onCreate(InstrumentationTestRunner.java:366)
            at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4219)
            at android.app.ActivityThread.access$1300(ActivityThread.java:140)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1287)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4898)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NoClassDefFoundError: in/sinking/project/ImplementationOfAnInterfaceFromCore
            at java.lang.Class.classForName(Native Method)
            at java.lang.Class.forName(Class.java:217)
            at android.test.ClassPathPackageInfoSource.createPackageInfo(ClassPathPackageInfoSource.java:88)
            at android.test.ClassPathPackageInfoSource.access$000(ClassPathPackageInfoSource.java:39)
            at android.test.ClassPathPackageInfoSource$1.load(ClassPathPackageInfoSource.java:50)
            at android.test.ClassPathPackageInfoSource$1.load(ClassPathPackageInfoSource.java:47)
            at android.test.SimpleCache.get(SimpleCache.java:31)
            at android.test.ClassPathPackageInfoSource.getPackageInfo(ClassPathPackageInfoSource.java:72)
            at android.test.ClassPathPackageInfo.getSubpackages(ClassPathPackageInfo.java:48)
            at android.test.ClassPathPackageInfo.addTopLevelClassesTo(ClassPathPackageInfo.java:61)
            at android.test.ClassPathPackageInfo.getTopLevelClassesRecursive(ClassPathPackageInfo.java:55)
            at android.test.suitebuilder.TestGrouping.testCaseClassesInPackage(TestGrouping.java:156)
            at android.test.suitebuilder.TestGrouping.addPackagesRecursive(TestGrouping.java:117)
            at android.test.suitebuilder.TestSuiteBuilder.includePackages(TestSuiteBuilder.java:102)
            at android.test.InstrumentationTestRunner.onCreate(InstrumentationTestRunner.java:366)
            at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4219)
            at android.app.ActivityThread.access$1300(ActivityThread.java:140)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1287)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4898)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: 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 java.lang.Class.classForName(Native Method)
            at java.lang.Class.forName(Class.java:217)
            at android.test.ClassPathPackageInfoSource.createPackageInfo(ClassPathPackageInfoSource.java:88)
            at android.test.ClassPathPackageInfoSource.access$000(ClassPathPackageInfoSource.java:39)
            at android.test.ClassPathPackageInfoSource$1.load(ClassPathPackageInfoSource.java:50)
            at android.test.ClassPathPackageInfoSource$1.load(ClassPathPackageInfoSource.java:47)
            at android.test.SimpleCache.get(SimpleCache.java:31)
            at android.test.ClassPathPackageInfoSource.getPackageInfo(ClassPathPackageInfoSource.java:72)
            at android.test.ClassPathPackageInfo.getSubpackages(ClassPathPackageInfo.java:48)
            at android.test.ClassPathPackageInfo.addTopLevelClassesTo(ClassPathPackageInfo.java:61)
            at android.test.ClassPathPackageInfo.getTopLevelClassesRecursive(ClassPathPackageInfo.java:55)
            at android.test.suitebuilder.TestGrouping.testCaseClassesInPackage(TestGrouping.java:156)
            at android.test.suitebuilder.TestGrouping.addPackagesRecursive(TestGrouping.java:117)
            at android.test.suitebuilder.TestSuiteBuilder.includePackages(TestSuiteBuilder.java:102)
            at android.test.InstrumentationTestRunner.onCreate(InstrumentationTestRunner.java:366)
            at android.app.ActivityThread.handleBindApplication(ActivityThread.java:4219)
            at android.app.ActivityThread.access$1300(ActivityThread.java:140)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1287)
            at android.os.Handler.dispatchMessage(Handler.java:99)
            at android.os.Looper.loop(Looper.java:137)
            at android.app.ActivityThread.main(ActivityThread.java:4898)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:511)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1006)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:773)
            at dalvik.system.NativeStart.main(Native Method)

2 thoughts on “Android Dependency Double Trouble”

  1. Hi,

    Thank you for sharing your experience here. Although my cause was different the outcome was the same for me and you helped explain it!

    Thanks
    Marc

Leave a Reply

Your email address will not be published. Required fields are marked *