One of the major challenge for the android developers to understand the how the Android Gradle Plugin
(AGP) works. As android devs we mostly use the ready-to-develop gradle plugin settings or just add thedependencyResolutionManagement
settings with respect toJCenter or Maven. So, why bother to write your own TransformAction for Android Gradle Plugin (AGP)?
Transform
API for such cases, but it’s proved to be inefficient and may be the cause of slow builds. The new API TransformAction is much more efficient and easier to consume, so one can directly start modifying bytecode with almost zero-setup. In july 2021, the Android Gradle Plugin
was updated to version 7.0. Everyone was surprised to find that the Transform class marked as depreceated, and no direct replacement class was provided in the comments. So, an article, I found on the internet mentioned an interface: TransformAction
is the replacement of Transform.TransformAction
exactly is?I think now you guys have some kind of understanding that what this article is all about. We gonna see the explore the TransformAction
and show how this transformation can be useful for automating tedious or even impossible from the source code prespective.
Brief Intro of TransformAction in Gradle (AGP)
The best way to learn about a module is to look at the official documentation. I know, it is too long and boring 😂 you can read my summary for it. Simply put it as TransformAction
is the Gradle provided product transition between properties and switch dependencies from one state to another.
In the process of developing various application our project may have multiple variant, i.e. a dependency may have two variants classes
or jar
. Now when the Gralde configuration is parsing out the dependency which doesn’t have the variant of the requested property, then the parsing configuration fails.
So, we can download (decompressed) Jar with TransformAction
and convert java-api
jar to a java-api
classes variant. This transformation can transform dependent products, so it is called product transformation
The process to write custom TransformAction
for Android Gralde is quite interesting.
I know you guys must be thinking about why the hack now the Kotlin DSL? Actually Gradle provide an alternative syntax to the traditional Groovy DSL with the enhanced experience supported in Android Studio. Mostly android devs are already using Kotlin Programming Language
for development of mobile application.
I’m not gonna go in the detail of how to write Kotlin DSL. For more info about DSL’s check out this link.
For the example we gonna implement a Unzip
TransformAction which transform a Jar
file into classes directory by unzipping it. The Unzip
transform does not require any parameters.
First create new buildSrc
directory in the same-level of app
directory and create new Unzip
name class or maybe any other name which you prefer.
Second copy the following the code and paste inside the Unzip
class.
abstract class Unzip : TransformAction<TransformParameters.None> { // 1 @get:InputArtifact abstract val inputArtifact: Provider<FileSystemLocation> //2 override fun transform(outputs: TransformOutputs) { val input = inputArtifact.get().asFile val unzipDir = outputs.dir(input.name) // 3 unzipTo(input, unzipDir) // 4 } private fun unzipTo(zipFile: File, unzipDir: File) { } }
Here you:
TransformParameters.None
when there is not parameters to use.It can be seen that the input file content and the output address can be obtained through the framework. So where the input come from? Do all dependent files need to be processed as input? Obviously not, as mentioned earlier, we need to register the artifact transform actions, providing parameters if necessary, so that they can be selected when resolving dependencies.
In order to register an artifact transform, you must use registerTransform()
within the dependencies{}
block. We can do so by registering an artifact transform action of type Unzip
, as show below in build.gradle.kts
file.
val artifactType = Attribute.of(“artifactType”, String::class.java) dependencies { registerTransform(Unzip::class) { from.attribute(artifactType, “jar”) to.attribute(artifactType, “my-custom-type”) } }
When we receive an artifact of jar
this class (artifact), it is called Unzip
, and it is converted to my-custom-type
. The my-custom-type
is just a state flag, which is convenient for us to process it later.
Another example is that you want to unzip JARs by only keeping some class files from them by passing the custom parameters. Note the use of the parameters{}
block to provide the classes to keep in the unzip transformer.
val artifactType = Attribute.of(“artifactType”, String::class.java) val keepPatterns = mapOf( “custom-type” to setOf( “com.google.common.base.Optional”, “com.google.common.base.AbstractIterator” ) ) dependencies { registerTransform(Unzip::class) { from.attribute(artifactType, “jar”) to.attribute(artifactType, “my-custom-type”) parameters { keepClassesByArtifact = keepPatterns } } }
Now we need to have an updated Unzip
class which accepts parameters of Map<String,Se<String>>>
.
abstract class Unzip : TransformAction<Unzip.Parameters> { // 1 interface Parameters : TransformParameters { // 2 @get:Input var keepClassesByArtifact: Map<String, Set<String>> } @get:PathSensitive(PathSensitivity.NAME_ONLY) @get:InputArtifact abstract val inputArtifact: Provider<FileSystemLocation> override fun transform(outputs: TransformOutputs) { val input = inputArtifact.get().asFile val unzipDir = outputs.dir(input.name) // use keepClassesParameters parameters.keepClassesByArtifact.forEach { key, value -> // 3 } unzipTo(input, unzipDir) } private fun unzipTo(zipFile: File, unzipDir: File) { } }
The code above does the following:
Phew! 😅 that was a lot of stuff we covered in this article. This article mainly explains about TransformAction
with Kotlin DSLs and the contruction process, we still need to understand and the basic usage of principle of Android Gradle Plugin (AGP).
I hope you guys have something learnt from it. Thank you being here and keep reading!
Quick Links
Legal Stuff
Social Media