immutables: TypedAdapterFactory is not loaded with ServiceLoader on Android

TypedAdapterFactory seems like is not generated (or included) into final app.apk/META-INF/services. Could be an android build tools bug, and here is a workaround

Also, those are normally generated and present: javax.ws.rs.ext.MessageBodyReader javax.ws.rs.ext.MessageBodyWriter

build.gradle is configured like this:

provided 'org.immutables:value:2.0.6'
apt 'org.immutables:value:2.0.6'
compile 'org.immutables:gson:2.0.6'
apt 'org.immutables:gson:2.0.6'

TypeAdapters are generated as well and I can link 'em manually.

About this issue

  • Original URL
  • State: open
  • Created 9 years ago
  • Comments: 20 (8 by maintainers)

Most upvoted comments

I recently stumbled upon a similar issue and created a workaround that would copy the initial generated com.google.gson.TypeAdapterFactory into the app\src\main\resources\META-INF\services. In this way, ServiceLoader will load implementations successfully.

Just paste this to your build.gradle.

afterEvaluate {
  android.applicationVariants.all { variant ->
    variant.javaCompiler.dependsOn(copyAdapterFactories)
  }
}

task copyAdapterFactories(type: Copy) {
  from 'build/intermediates/classes/debug/META-INF/services/com.google.gson.TypeAdapterFactory'
  into { 'src/main/resources/META-INF/services' }
}

After you have pasted the snippet, you can use ServiceLoader to include all TypeAdapterFactorys generated by the Immutables library.

import com.google.gson.GsonBuilder;
import java.util.ServiceLoader;

GsonBuilder gsonBuilder = new GsonBuilder();
for (TypeAdapterFactory factory : ServiceLoader.load(TypeAdapterFactory.class)) {
  gsonBuilder.registerTypeAdapterFactory(factory);
}

Update: When you add a new model to your codebase, com.google.gson.TypeAdapterFactory should be updated accordingly. It does update here build/intermediates/classes/debug/META-INF/services but it doesn’t get copied always because the snippet above copies the file only on afterEvaluate. Moreover, the com.google.gson.TypeAdapterFactory should be always overwritten by a new file. So here’s an updated version of the snippet:

tasks.whenTaskAdded { task ->
  if (task.name == "compileDebugSources" || task.name == "compileReleaseSources") {
    task.dependsOn(copyingTask)
  }
}

task copyingTask(type: Copy, overwrite: true) {
  from 'build/intermediates/classes/debug/META-INF/services/com.google.gson.TypeAdapterFactory'
  into { 'src/main/resources/META-INF/services' }
}

FYI it appears there was a bug in which META-INF subdirectories were being explicitly excluded. See this issue thread. This appears to have been fixed. I’m currently getting META-INF correctly included when placed under main/resources. Not sure if /resources is the only path that works.