Search code examples
javaandroidgradlegradle-pluginmulti-module

Android multi-module project fails with TypeNotPresentException


I have an android project with 16 modules. Many of these are java modules, and some include annotation processing. I have targeted Java 1.8 as well, to include support for lambdas and other Java 8 features.

Most of my dependencies are declared in my dependencies closure, however I am adding some dependencies using a custom plugin. This adds runtimeOnly and implementation dependencies during the build:

class DynamicDeps implements Plugin<Project> {
    @Override
    void apply(Project target) {
        target.dependencies.add("implementation", target.rootProject.project("myUiModule"))
        target.dependencies.add("runtimeOnly", target.rootProject.project("myBusinessLogicModule"))
    }
}

apply plugin: DynamicDeps

Every module except app builds fine independently, however when I run

gradlew clean :app:assemble

I get the following exception:

Exception in thread "main" java.lang.TypeNotPresentException: Type org.jdom.Element not present
        at sun.invoke.util.BytecodeDescriptor.parseSig(BytecodeDescriptor.java:85)
        at sun.invoke.util.BytecodeDescriptor.parseMethod(BytecodeDescriptor.java:54)
        at sun.invoke.util.BytecodeDescriptor.parseMethod(BytecodeDescriptor.java:41)
        at java.lang.invoke.MethodType.fromMethodDescriptorString(MethodType.java:1067)
        at com.google.devtools.build.android.desugar.LambdaDesugaring$InvokedynamicRewriter.toMethodHandle(LambdaDesugaring.java:599)
        at com.google.devtools.build.android.desugar.LambdaDesugaring$InvokedynamicRewriter.toJvmMetatype(LambdaDesugaring.java:586)
        at com.google.devtools.build.android.desugar.LambdaDesugaring$InvokedynamicRewriter.visitInvokeDynamicInsn(LambdaDesugaring.java:401)
        at org.objectweb.asm.ClassReader.a(Unknown Source)
        at org.objectweb.asm.ClassReader.b(Unknown Source)
        at org.objectweb.asm.ClassReader.accept(Unknown Source)
        at org.objectweb.asm.ClassReader.accept(Unknown Source)
        at com.google.devtools.build.android.desugar.Desugar.desugarClassesInInput(Desugar.java:401)
        at com.google.devtools.build.android.desugar.Desugar.desugarOneInput(Desugar.java:326)
        at com.google.devtools.build.android.desugar.Desugar.desugar(Desugar.java:280)
        at com.google.devtools.build.android.desugar.Desugar.main(Desugar.java:584)
Caused by: java.lang.ClassNotFoundException: Class org.jdom.Element not found
        at com.google.devtools.build.android.desugar.HeaderClassLoader.findClass(HeaderClassLoader.java:53)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
        at sun.invoke.util.BytecodeDescriptor.parseSig(BytecodeDescriptor.java:83)
        ... 14 more

I am not using the libraries mentioned, but my understanding is that these could be transitive dependencies of annotation processors (such as Dagger, which I am using) or Mockito.

What steps can I take to resolve this issue? Has anyone else had this problem and was able to resolve? Due to the nature of this project, I cannot post many additional details, however I am using:

  • Gradle 4.1 with
  • Android Gradle plugin 3.0.1.
  • Min sdk 22
  • Target sdk 22
  • Compile sdk 26
  • Build Tools 26.0.2

Solution

  • I was able to fix this with two changes:

    1. As suspected, this was a problem with transitive dependencies. The dynamically-added dependency add method can accept a third parameter to configure its settings. Using this to force non-transitive dependencies was the solution:

      class DynamicDeps implements Plugin<Project> {
          @Override
          void apply(Project target) {
              target.dependencies.add("implementation", target.rootProject.project("myUiModule"), { dep -> 
                  dep.transitive = false
              })
              target.dependencies.add("runtimeOnly", target.rootProject.project("myBusinessLogicModule"), { dep -> 
                  dep.transitive = false
              })
          }
      }
      
      apply plugin: DynamicDeps
      
    2. I also used Retrolambda to convert the bytecode from my Java 8 modules into Java 7 to support better compatibility with Android. I am not sure if this step was needed, but it was part of the solution that worked for me.