Capt, Class Annotation Processor Tool, is a bytecode Annotation processing Tool for Android platform built by the author based on ASM and Android Transform API.

The origin of

apt

Capt’s inspiration comes largely from APT. As an Android developer, apt is powerful enough, but there is one flaw that has always been a sore point for us: it only supports source code.

Android’s daily development relies heavily on third-party libraries such as AAR/JAR, which have been compiled into class bytecode, while APT only supports source-level annotation processing, because we have to go through a lot of twists and turns to implement simple logic.

For example: Ali open source ARouter, in the early version only APT, modularity is handled by explicitly adding module parameters. In the new version, it also registers the Android Transform API to modify part of the bytecode to reach the target during the packaging process.

Lancet

The Lancet is an Android AOP framework that the authors opened source at Eleme earlier this year, based on the Transform API. Following the abandonment of GitHub Eleme a few months ago, the author created an independent Lancet project and planned the development of Lancet2. In the process of thinking about it, there are a few reasons why just being Lancet2 isn’t cool enough:

  1. There are several fixed annotations and their use is very limited
  2. Both Lancet2 and Lancet1 have a lot of repetitive code wasted on irrelevant logic in the main flow

So the author came up with an idea: could we make a tool that only does the distribution logic of annotation processing and code transformation, and the specific business logic is done by plug-ins?

No sooner said than done, capT was born.

capt

Compared to APT, CAPT allows multiple plug-ins to modify the bytecode of each class in a chain, in addition to providing annotation processing. Meanwhile, CAPT has the following features:

Fully synchronous Variant

Similar to annotationProcessor, capt creates a Configuration for each SourceSet/BuildType/ProductFlavor, which you can do in the following way, depending on the BuildType, To use different CAPT plug-ins:

dependencies {
    capt project(":xx")
    capt "Xx: xx: 1.0"
    capt files("...")
    releaseCapt ...
    androidTestCapt ...
}
Copy the code

Application & Library

Capt supports both Android Application and Library annotation processing and injection code:

  • Application: All runtime classes
  • Library: Only the Library itself

APK & AndroidTest

Capt supports injection of normal APK and AndroidTest:

  • APK: All classes in APK are passed when normal APK is played
  • AndroidTest: When AndroidTest is packaged, it only passes all classes in the AndroidTest directory

Flexible parameters

Capt creates a Gradle Extension object for each registered plug-in. It can pass in any form of parameters to the plug-in during the plug-in’s life cycle. Each Extension also has built-in plugin public parameters. Such as priority (covering plug-in declared in default priority) and scope (Assemble | AndroidTest), etc.

The ultimate incremental update

Capt has its own cache for each Variant, recording meta information such as class diagram, plug-in and modified classes of the plug-in.

Annotation processing

Capt parses the class diagram and analyzes changes (additions, modifications, and deletions) of classes in the class diagram, so CAPT requires all plug-in annotation processors to support incremental resolution, so CAPT has little impact on packaging time. And performance has always been the goal of CPAT

There are gains and losses, of course, because in incremental mode, classes that contain annotations are not passed to the annotation handler without changes, so it is up to each plug-in to implement the logic of local caching.

Class conversion

Capt follows the rule of minimum, so there are three steps to class conversion:

  1. Capt asks each plug-in which classes (full, incremental, none) it needs and whether additional classes are resolved
  2. If a plug-in is removed from the last build (removing a plug-in does not interrupt the incremental build process, adding it will), CAPT adds additional classes modified by the removed plug-in
  3. Finally, CAPT will summarize the above information and distribute the transformation process of the class based on it.

Rich API

Capt provides rich apis throughout the plug-in life cycle, such as class structure diagrams, contexts, and even classloaders, allowing each plug-in’s whizz to be implemented. You can also Issue & PR if you have more and better ideas.

High level of support for ASM

Capt provides maximum flexibility by allowing additional flag bits to be requested for classReaders and ClassWriters before classes are converted, while default to 0 for best performance.

And capT creates a unique ClassLoader for classWriter.pute_frame, so developers don’t have to worry about calculating stack frame information in their code.

The future planning

  • Lancet2 based on CAPT
  • Continuous performance optimization
  • More rich apis

Open source address

Github.com/CoffeePartn…

If you think CAPt is good, please give Capt a Star! Welcome to Issue & PR.