byte-buddy icon indicating copy to clipboard operation
byte-buddy copied to clipboard

Byte Buddy on Android inflates build times

Open MustafaHaddara opened this issue 7 months ago • 2 comments

I've got a tiny Android application that uses the opentelemetry API, including their okhttp instrumentation. This instrumentation defines 2 bytebuddy plugins (linked above and reproduced here):

public class OkHttpClientPlugin implements Plugin {

    @Override
    public DynamicType.Builder<?> apply(
            DynamicType.Builder<?> builder,
            TypeDescription typeDescription,
            ClassFileLocator classFileLocator) {
        return builder.visit(
                Advice.to(OkHttpClientAdvice.class)
                        .on(
                                ElementMatchers.isConstructor()
                                        .and(
                                                ElementMatchers.takesArguments(
                                                        OkHttpClient.Builder.class))));
    }

    @Override
    public void close() throws IOException {
        // No operation.
    }

    @Override
    public boolean matches(TypeDescription target) {
        return target.getTypeName().equals("okhttp3.OkHttpClient");
    }
}
public class OkHttpCallbackPlugin implements Plugin {
    private static final Pattern REAL_CALL_PATTERN = Pattern.compile("^okhttp3\\..*RealCall$");

    @Override
    public DynamicType.Builder<?> apply(
            DynamicType.Builder<?> builder,
            TypeDescription typeDescription,
            ClassFileLocator classFileLocator) {
        return builder.visit(
                Advice.to(OkHttpCallbackAdvice.class)
                        .on(named("enqueue").and(takesArgument(0, Callback.class))));
    }

    @Override
    public void close() throws IOException {
        // No operation.
    }

    @Override
    public boolean matches(TypeDescription target) {
        return REAL_CALL_PATTERN.matcher(target.getTypeName()).matches();
    }
}

To me, these plugin definitions look like they should be limited to two specific okhttp targets, and they seem correct. However, when I include byte buddy in my gradle build, my dexBuilderDebug step gets incredibly slower.

without bytebuddy

Image

Looking at the logs for the dexBuilderDebug step:

2025-06-05T14:45:35.779-0400 [INFO] [com.android.build.gradle.internal.tasks.DexArchiveBuilderTaskDelegate] Dex builder is incremental : false 
2025-06-05T14:45:35.841-0400 [INFO] [com.android.build.gradle.internal.tasks.DexArchiveBuilderTaskDelegate] Processing input <project-root>/example/build/tmp/kotlin-classes/debug
2025-06-05T14:45:35.841-0400 [INFO] [com.android.build.gradle.internal.tasks.DexArchiveBuilderTaskDelegate] Dexing <project-root>/example/build/tmp/kotlin-classes/debug
2025-06-05T14:45:35.843-0400 [INFO] [com.android.build.gradle.internal.tasks.DexArchiveBuilderTaskDelegate] Processing input <project-root>/example/build/intermediates/compile_and_runtime_not_namespaced_r_class_jar/debug/processDebugResources/R.jar
2025-06-05T14:45:35.844-0400 [INFO] [com.android.build.gradle.internal.tasks.DexArchiveBuilderTaskDelegate] Dexing <project-root>/example/build/intermediates/compile_and_runtime_not_namespaced_r_class_jar/debug/processDebugResources/R.jar

If I look in example/build/tmp/kotlin-classes/debug, I see all of my app's class files. Looking in R.jar, I see a small number of other classes:

jar -tf example/build/intermediates/compile_and_runtime_not_namespaced_r_class_jar/debug/processDebugResources/R.jar | wc -l
     322

The full list appears to be android constants and other minor classes.

with bytebuddy

Image

Looking at the logs for the dexBuilderDebug step:

2025-06-05T14:32:54.289-0400 [INFO] [com.android.build.gradle.internal.tasks.DexArchiveBuilderTaskDelegate] Dex builder is incremental : false 
2025-06-05T14:32:54.315-0400 [INFO] [com.android.build.gradle.internal.tasks.DexArchiveBuilderTaskDelegate] Processing input <project-root>/example/build/intermediates/classes/debug/ALL/debugBytebuddyTransform/classes.jar
2025-06-05T14:32:54.315-0400 [INFO] [com.android.build.gradle.internal.tasks.DexArchiveBuilderTaskDelegate] Dexing <project-root>/example/build/intermediates/classes/debug/ALL/debugBytebuddyTransform/classes.jar

and if we look inside that JAR:

$ jar -tf example/build/intermediates/classes/debug/ALL/debugBytebuddyTransform/classes.jar | wc -l
   32503

If I drop the | wc -l, I see a list of what appears to be every single class on the classpath.


So, in summary: I believe bytebuddy is reading all of the classes on the class path, not processing them, and then shoving them all into one massive Jar. Then, the android compiler gets significantly slower when trying to dex them. Is there some config option I am missing? Are the bytebuddy plugins written incorrectly?

I'm using ByteBuddy 1.17.5

MustafaHaddara avatar Jun 05 '25 18:06 MustafaHaddara

Not quite my field of expertise, but maybe @LikeTheSalad could comment here?

raphw avatar Jun 15 '25 22:06 raphw

Hi @MustafaHaddara

I believe bytebuddy is reading all of the classes on the class path, not processing them, and then shoving them all into one massive Jar

You're right. Byte Buddy needs to read the entire classpath and then merge everything, even the unprocessed classes, into a single JAR file. This is by Google's design, though, on their transform-all-classes API. Someone created this issue to ask for a newer API that doesn't require merging everything into an uber JAR, though it seems like it could take a while.

In the meantime, one approach you could take is to avoid running it for your debug build type, so that it's only enabled when you're planning to create a build for production, and that way you shouldn't get affected by it during development.

For example, this way you add a Byte Buddy plugin only for release builds:

// build.gradle.kts file where the Byte Buddy plugin is applied.

val byteBuddyRelease = configurations.create("releaseByteBuddy")

dependencies {
  byteBuddyRelease("example-byte-buddy:plugin:0.0.0") // Use the release config instead of plain "byteBuddy".
}

LikeTheSalad avatar Jun 16 '25 04:06 LikeTheSalad