Skip to content

Migrate Hunter from Transform API to AGP 8.7 Instrumentation API#68

Merged
Leaking merged 1 commit into
masterfrom
claude/optimize-android-asm-dZVp9
Apr 26, 2026
Merged

Migrate Hunter from Transform API to AGP 8.7 Instrumentation API#68
Leaking merged 1 commit into
masterfrom
claude/optimize-android-asm-dZVp9

Conversation

@Leaking

@Leaking Leaking commented Apr 26, 2026

Copy link
Copy Markdown
Owner

This PR modernizes Hunter to use the Android Gradle Plugin 8.7+ Instrumentation API instead of the deprecated Transform API, while maintaining backward compatibility with the existing plugin architecture.

Summary

Hunter has been refactored to leverage AGP's modern AsmClassVisitorFactory instrumentation pipeline. The legacy Transform API implementation has been replaced with a new base plugin framework that abstracts away AGP instrumentation boilerplate, allowing plugin authors to continue writing ClassVisitor-based weavers.

Key Changes

Core Framework

  • Removed: HunterTransform class and all Transform API-specific code
  • Removed: ExtendClassWriter, ClassLoaderHelper, Worker, and Schedulers (no longer needed with AGP's built-in instrumentation)
  • Added: HunterPlugin<P, F> - base plugin class that handles AGP instrumentation registration
  • Added: HunterAsmClassVisitorFactory<P> - bridges AGP's factory contract to Hunter's IWeaver abstraction
  • Added: HunterParameters - common interface for variant-specific configuration

Plugin Updates

All four plugins (debug, timing, okhttp, linelog) have been refactored:

  • Removed individual *HunterTransform classes
  • Updated *HunterPlugin to extend HunterPlugin base class
  • Added *ClassVisitorFactory implementations
  • Added *HunterParameters interfaces for configuration
  • Simplified *Weaver classes to focus on ClassVisitor wrapping

ASM & Bytecode

  • Updated BaseWeaver to provide simplified wrap(ClassVisitor) contract
  • Simplified IWeaver interface to focus on visitor wrapping
  • Updated ASM version from 7.1 to 9.7
  • Updated method adapters to use ASM9 opcodes
  • Fixed constructor instrumentation in TimingMethodAdapter to defer probe injection until after super/this calls

Build & Dependencies

  • Updated AGP from 4.1.3 to 8.7.0
  • Removed commons-io dependency (no longer needed)
  • Updated gradle-maven-publish-plugin from 0.17.0 to 0.30.0
  • Updated Gradle wrapper to 8.7
  • Added Java 17 source/target compatibility
  • Updated publishing workflow for Maven Central via Sonatype
  • Added namespace declarations to all Android modules

Examples

Updated all example projects to apply plugins via buildscript classpath and use modern Android DSL (compileSdk instead of compileSdkVersion).

Implementation Details

  • The new HunterPlugin base class handles variant filtering via RunVariant.shouldSkip()
  • HunterAsmClassVisitorFactory lazily caches weaver instances to avoid re-reading parameters
  • Plugin authors only need to implement createWeaver(ClassContext) and optionally override registerExtension()
  • The instrumentation scope and frames computation mode are configurable per plugin

https://claude.ai/code/session_011eDE2NVFKX5Fyk8ggYaV89

Summary by CodeRabbit

Release Notes

  • New Features

    • Migrated to modern AGP Instrumentation API for improved compatibility with latest Android tooling.
  • Breaking Changes

    • Requires Java 17, AGP 8.7.0+, and Android SDK 34 (minSDK 21).
    • Removed legacy Gradle Transform API support.
    • Removed duplicated class safe mode configuration.
  • Improvements

    • Updated bytecode instrumentation framework to ASM 9.7.
    • Enhanced Maven Central publishing pipeline with automated workflow.
    • Modernized example apps to use AndroidX and current best practices.

Issues addressed:

- #67 / #63 / #34: Transform API removed in AGP 8.0 → migrated each Hunter
  plugin to AndroidComponentsExtension.onVariants + AsmClassVisitorFactory.
  HunterTransform / ClassLoaderHelper / ExtendClassWriter / Schedulers / Worker
  are deleted; the new HunterPlugin / HunterAsmClassVisitorFactory base
  classes hide the AGP boilerplate so existing weaver authors keep writing
  ClassVisitor-based weavers.

- #66 / #60: bumped every ASM ClassVisitor / MethodVisitor adapter from
  Opcodes.ASM7 to Opcodes.ASM9 so Java 17 records and sealed classes load.
  ASM dependency bumped to 9.7.

- #48: TimingMethodAdapter no longer injects the start probe at visitCode in
  constructors — it tracks NEW depth and waits for the outermost
  INVOKESPECIAL <init> before priming startVarIndex, producing legal
  bytecode.

- DebugWeaver's two-pass pre-go / instrumentation pipeline now runs against
  a buffered ClassNode, since AGP only hands us a single ClassVisitor chain.

- DebugPreGoMethodAdapter.visitLocalVariable guards against an empty
  labelList instead of throwing IndexOutOfBoundsException on exotic methods.

Build / publish:
- AGP 8.7, Gradle 8.10.2, JDK 17 toolchain, compileSdk 34, minSdk 21.
- vanniktech maven-publish 0.30 + Sonatype Central Portal (OSSRH was shut
  down 2025-06-30). Signing only enabled when a GPG key is configured, so
  publishToMavenLocal still works on a dev box.
- New .github/workflows/gradle-publish.yml uses repository secrets only —
  MAVEN_CENTRAL_USERNAME / MAVEN_CENTRAL_PASSWORD / SIGNING_IN_MEMORY_KEY /
  SIGNING_IN_MEMORY_KEY_ID / SIGNING_IN_MEMORY_KEY_PASSWORD — and triggers
  on `v*` tags or manual dispatch (with a dry-run option).
- New .github/workflows/build.yml runs assemble + check on PRs and pushes.

Examples are excluded from settings.gradle: they reference plugins by
their published Maven coordinates, so they only build after a publish.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai

coderabbitai Bot commented Apr 26, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 72dd2575-0ae2-4847-88ef-66fa1b39a5d0

📥 Commits

Reviewing files that changed from the base of the PR and between 5f19a1c and f9d74f3.

📒 Files selected for processing (72)
  • .github/workflows/build.yml
  • .github/workflows/gradle-publish.yml
  • README.md
  • build.gradle
  • buildSrc/src/main/java/Dependencies.kt
  • gradle-scripts/base-plugin-dependencies.gradle
  • gradle-scripts/plugin-dependencies.gradle
  • gradle.properties
  • gradle/wrapper/gradle-wrapper.properties
  • hunter-debug-example/build.gradle
  • hunter-debug-example/src/main/AndroidManifest.xml
  • hunter-debug-library/build.gradle
  • hunter-debug-library/src/main/AndroidManifest.xml
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/DebugHunterExtension.java
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/DebugHunterPlugin.java
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/DebugHunterTransform.java
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/DebugClassAdapter.java
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/DebugClassVisitorFactory.java
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/DebugMethodAdapter.java
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/DebugWeaver.java
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/prego/DebugPreGoClassAdapter.java
  • hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/prego/DebugPreGoMethodAdapter.java
  • hunter-linelog-example/build.gradle
  • hunter-linelog-example/src/main/AndroidManifest.xml
  • hunter-linelog-library/build.gradle
  • hunter-linelog-library/src/main/AndroidManifest.xml
  • hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/LinelogHunterExtension.java
  • hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/LinelogHunterPlugin.java
  • hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/LinelogHunterTransform.java
  • hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/bytecode/LinelogClassAdapter.java
  • hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/bytecode/LinelogClassVisitorFactory.java
  • hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/bytecode/LinelogMethodAdapter.java
  • hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/bytecode/LinelogWeaver.java
  • hunter-okhttp-example/build.gradle
  • hunter-okhttp-example/src/main/AndroidManifest.xml
  • hunter-okhttp-library-compat/build.gradle
  • hunter-okhttp-library-compat/src/main/AndroidManifest.xml
  • hunter-okhttp-library/build.gradle
  • hunter-okhttp-library/src/main/AndroidManifest.xml
  • hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/OkHttpHunterExtension.java
  • hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/OkHttpHunterParameters.java
  • hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/OkHttpHunterPlugin.java
  • hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/OkHttpHunterTransform.java
  • hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/bytecode/OkHttpClassAdapter.java
  • hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/bytecode/OkHttpClassVisitorFactory.java
  • hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/bytecode/OkHttpMethodAdapter.java
  • hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/bytecode/OkHttpWeaver.java
  • hunter-timing-example/build.gradle
  • hunter-timing-example/src/main/AndroidManifest.xml
  • hunter-timing-library/build.gradle
  • hunter-timing-library/src/main/AndroidManifest.xml
  • hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/TimingHunterExtension.java
  • hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/TimingHunterParameters.java
  • hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/TimingHunterPlugin.java
  • hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/TimingHunterTransform.java
  • hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/bytecode/TimingClassAdapter.java
  • hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/bytecode/TimingClassVisitorFactory.java
  • hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/bytecode/TimingMethodAdapter.java
  • hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/bytecode/TimingWeaver.java
  • hunter-transform/build.gradle
  • hunter-transform/src/main/java/com/quinn/hunter/transform/HunterAsmClassVisitorFactory.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/HunterParameters.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/HunterPlugin.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/HunterTransform.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/RunVariant.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/asm/BaseWeaver.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/asm/ClassLoaderHelper.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/asm/ExtendClassWriter.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/asm/IWeaver.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/concurrent/Schedulers.java
  • hunter-transform/src/main/java/com/quinn/hunter/transform/concurrent/Worker.java
  • settings.gradle

📝 Walkthrough

Walkthrough

Migrates Hunter from the legacy Gradle Transform API to the modern AGP AsmClassVisitorFactory and AndroidComponentsExtension. Updates all four plugins (debug, linelog, okhttp, timing) to the new architecture, upgrades build toolchain to Gradle 8.10.2 with AGP 8.7.0 and ASM 9.7, removes Transform infrastructure, and modernizes all Android examples to API 34 with Java 17.

Changes

Cohort / File(s) Summary
CI/CD Workflows
.github/workflows/build.yml, .github/workflows/gradle-publish.yml
Introduces new "Build" workflow triggering on master and claude/** pushes; repurposes publish workflow to release to Maven Central on v* tags with GPG signing and optional dry-run via workflow_dispatch.
Gradle Build System & Dependency Configuration
build.gradle, buildSrc/src/main/java/Dependencies.kt, gradle.properties, gradle/wrapper/gradle-wrapper.properties, settings.gradle
Updates root build to use maven-publish-plugin 0.30.0 with Sonatype Central Portal targeting; upgrades Gradle wrapper to 8.10.2; migrates clean task to tasks.register; updates version to 1.3.0 and bumps AGP to 8.7.0, ASM to 9.7, SDK targets to 34, and min SDK to 21; enables build caching and parallelism; adds pluginManagement repositories.
Base Hunter Plugin Infrastructure
hunter-transform/src/main/java/com/quinn/hunter/transform/HunterPlugin.java, hunter-transform/src/main/java/com/quinn/hunter/transform/HunterAsmClassVisitorFactory.java, hunter-transform/src/main/java/com/quinn/hunter/transform/HunterParameters.java
Introduces new HunterPlugin abstract base class implementing AGP variant-aware ASM transformation lifecycle; adds HunterAsmClassVisitorFactory adapter wrapping IWeaver instances; defines HunterParameters interface exposing variant name to worker processes.
RunVariant & IWeaver Refactoring
hunter-transform/src/main/java/com/quinn/hunter/transform/RunVariant.java, hunter-transform/src/main/java/com/quinn/hunter/transform/asm/IWeaver.java, hunter-transform/src/main/java/com/quinn/hunter/transform/asm/BaseWeaver.java
Extends RunVariant with shouldSkip(Variant) filtering method; refactors IWeaver to replace byte-array weaving with wrap(ClassVisitor) pattern; reduces BaseWeaver to class-name filtering and visitor wrapping, removing Transform-era jar/file processing.
Removed Legacy Transform Infrastructure
hunter-transform/src/main/java/com/quinn/hunter/transform/HunterTransform.java, hunter-transform/src/main/java/com/quinn/hunter/transform/asm/ClassLoaderHelper.java, hunter-transform/src/main/java/com/quinn/hunter/transform/asm/ExtendClassWriter.java, hunter-transform/src/main/java/com/quinn/hunter/transform/concurrent/Schedulers.java, hunter-transform/src/main/java/com/quinn/hunter/transform/concurrent/Worker.java
Deletes 255+ lines of Transform API infrastructure including incremental transform handling, ClassLoader setup, superclass resolution, and async work-queue scheduling.
Hunter Transform Build Configuration
hunter-transform/build.gradle, gradle-scripts/base-plugin-dependencies.gradle, gradle-scripts/plugin-dependencies.gradle
Configures Java 17 toolchain; removes AGP from implementation dependencies; adjusts ASM dependency set by adding asm-tree and reordering utilities; converts AGP to compileOnly.
Debug Plugin Refactoring
hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/DebugHunterPlugin.java, hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/DebugHunterExtension.java, hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/DebugHunterTransform.java, hunter-debug-plugin/src/main/groovy/com/quinn/hunter/plugin/debug/bytecode/*
Converts plugin from Plugin<Project> Transform registrar to HunterPlugin extending class; removes DebugHunterTransform and duplcatedClassSafeMode field; adds DebugClassVisitorFactory and refactors DebugWeaver to use wrap(ClassVisitor) pattern with pre-pass parameter collection; upgrades ASM to 9.x.
Linelog Plugin Refactoring
hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/LinelogHunterPlugin.java, hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/LinelogHunterExtension.java, hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/LinelogHunterTransform.java, hunter-linelog-plugin/src/main/groovy/com/quinn/hunter/plugin/linelog/bytecode/*
Mirrors debug plugin migration: extends HunterPlugin; removes transform class and deprecated field; adds LinelogClassVisitorFactory; refactors LinelogWeaver to use visitor wrapping; upgrades ASM 9.x and removes unused logging.
OkHttp Plugin Refactoring
hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/OkHttpHunterPlugin.java, hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/OkHttpHunterExtension.java, hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/OkHttpHunterParameters.java, hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/OkHttpHunterTransform.java, hunter-okhttp-plugin/src/main/groovy/com/quinn/hunter/plugin/okhttp/bytecode/*
Converts to HunterPlugin<OkHttpHunterParameters, OkHttpClassVisitorFactory> with new parameters interface; removes transform class and duplicatedClassSafeMode; replaces extension-based config with constructor-injected weaveEventListener boolean; refactors weaver to visitor wrapping with early class-name filtering.
Timing Plugin Refactoring
hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/TimingHunterPlugin.java, hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/TimingHunterExtension.java, hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/TimingHunterParameters.java, hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/TimingHunterTransform.java, hunter-timing-plugin/src/main/groovy/com/quinn/hunter/plugin/timing/bytecode/*
Extends HunterPlugin<TimingHunterParameters, TimingClassVisitorFactory>; introduces typed whitelist/blacklist parameters; removes transform class; refactors TimingWeaver to accept constructor-injected lists; implements constructor-aware timing probe insertion via isConstructor flag to defer super-call instrumentation.
Example Project Build Modernization
hunter-*/example/build.gradle (4 files), hunter-*/example/src/main/AndroidManifest.xml (4 files)
Updates all example projects: removes hunter-transform buildscript dependency; adds explicit plugin application; migrates compileSdkVersioncompileSdk, lintOptionslint; adds namespace; sets Java 17; replaces support library with AndroidX; updates OkHttp 3.x → 4.12.0; marks launcher activities android:exported="true"; removes manifest package attribute.
Library Module Build & Manifest Updates
hunter-*/library/build.gradle (4 files), hunter-*/library/src/main/AndroidManifest.xml (4 files)
Standardizes library builds with namespace, compileSdk, defaultConfig.minSdkVersion, and Java 17 compileOptions; simplifies manifests to remove package attribute (namespace now handled by AGP).
Documentation & Versioning
README.md
Redefines Hunter as targeting modern AsmClassVisitorFactory instead of legacy Transform API; updates compatibility section detailing 1.3.0 as breaking release with Transform removal and ASM 9.x; corrects latest version to 1.3.0; extends OkHttp plugin references.

Sequence Diagram

sequenceDiagram
    participant User as User/Gradle
    participant HPlugin as HunterPlugin
    participant AGP as AndroidComponentsExtension
    participant Variant as Variant
    participant HFactory as HunterAsmClassVisitorFactory
    participant Weaver as IWeaver (e.g. DebugWeaver)
    participant ClassVisitor as AGP ClassVisitor Chain

    User->>HPlugin: apply(Project)
    HPlugin->>HPlugin: registerExtension(Project)
    HPlugin->>AGP: locate & obtain reference
    
    alt AGP Available
        HPlugin->>AGP: onVariants { ... }
        loop For Each Variant
            AGP->>Variant: call listener
            Variant->>HPlugin: skipVariant check
            alt Not Skipped
                HPlugin->>HFactory: transformClassesWith(getFactoryClass(), ...)
                HFactory->>HFactory: createClassVisitor(ClassContext, nextClassVisitor)
                HFactory->>Weaver: createWeaver(ClassContext)
                Weaver->>HFactory: return IWeaver instance
                HFactory->>Weaver: wrap(nextClassVisitor)
                Weaver->>ClassVisitor: return wrapped ClassVisitor
                ClassVisitor->>ClassVisitor: instrument bytecode
            end
        end
    else AGP Not Applied
        HPlugin-->>User: throw IllegalStateException
    end
Loading

Old Flow (Removed): User/Gradle → Plugin.apply() → register Transform via AppExtension → Transform.transform(Context, ...) → custom ClassLoader setup → iterate JARs/files → ClassReader/ClassWriter/ExtendClassWriter → async work queue → await completion

New Flow: User/Gradle → HunterPlugin.apply() → register extension → AndroidComponentsExtension.onVariants → for each variant check skipVariant → register ASM transformation with factory → factory creates weaver on-demand (cached) → weaver wraps ClassVisitor for bytecode instrumentation

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Poem

🐰 Hop along, old Transform API—it's time to stay
With AGP's modern ClassVisitor way!
ASM 9 bounds, Java 17 runs fast,
From legacy caches to futures so vast—
Hunter now weaves through the components with grace.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/optimize-android-asm-dZVp9

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Leaking Leaking merged commit b906fa5 into master Apr 26, 2026
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants