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)
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