[MUSIC PLAYING]
ALEX SAVEAU: Hi, folks.
I'm Alex, and I maintain the Gradle Play Publisher
and Version Orchestrator plugins.
So today as part of the MAD Skills series on Gradle
and Build APIs, I want to talk to you about using modern
Android Gradle plugin-- or AGP--
and Gradle APIs to manipulate your Android build artifacts.
So these new APIs heavily focus on providers
and improved build performance and correctness,
and naturally fall out of strictly specifying your task
inputs and outputs using providers.
So starting in AGP 7.0, a set of vastly improved APIs
are available for plug-in authors to take advantage--
and that's you whenever you're writing a Gradle task.
So these new APIs offer two big benefits.
First, there's API stability.
And then second, there's a clean slate for modernization.
So as mentioned, these APIs all take advantage of providers.
And that's a good thing.
So I wanted to just touch on API stability again,
because it's a huge deal.
It means that when you update AGP,
your plugins aren't just going to randomly break
because there was some ABI change somewhere in AGP.
So now you can confidently upgrade AGP.
And for the most part, your plugins
will just continue working as normal.
OK, so let's start by taking a look at the AGP Variant Outputs
API.
Which primarily focuses on letting
you specify the version name and version code of your APK
or bundle file.
So before jumping into that, let's
take a look at creating a task that outputs a version name.
So all modern tasks these days should
be abstract with abstract properties.
So Gradle will automatically generate an implementation
of your task at runtime.
And it will instantiate all your properties.
And if you use dependency injection,
it'll give you the right objects you ask for in the constructor.
Something to notice that your properties are just
going to be instantiated, not configured.
You still have to do that.
So by default, they'll be configured to null.
And then in this task, we're going
to use the properties we specified.
So in this case, myVersionName, which is an output
file property.
And we're going to write our computation to the file
specified in that property.
And we don't have to worry about creating the file.
Gradle is going to take care of that.
And we also don't have to worry about managing dependencies.
That's the benefit of using properties
that are annotated with input or output annotations.
Gradle will automatically take care
of running tasks that produce your input properties.
So this task supports up-to-date checks already.
And if we wanted to, we could even
make it support cacheable builds by simply
adding the add cacheable task annotation.
We can now register our task and configure AGP's properties
to use our version name.
So now let's register our task and configure the output
property.
Value pick doesn't really matter.
So I just chose the build slash intermediates directory, which
is just kind of a dumping ground for stuff
that humans aren't supposed to look at.
Once we've registered the task, we'll get a task provider.
And that provider is going to carry
the dependency information of the task
so we can simply use the on variance API from AGP
and set its property to our property.
And the magic here is that Gradle--
since we're using a task provider--
will automatically handle running your task.
You don't have to worry about setting up dependencies
or anything like that.
So the benefit of using properties and providers
is that the outputs of your task are
separate from the task implementation that
creates them.
And this provides that nice abstraction
where we can just set a property on AGP and everything
just works.
So a quick note about mapping versus flat-mapping providers.
If you map A to B, the dependency will come from A.
But if you flat-map A to B, then the dependency comes from B.
All righty.
Moving on to the next API, we have AGP's Artifacts API.
So this one is super relevant to Gradle Play
Publisher, because it lets you access things
like bundle files and APKs.
So for example, you could write a task with an input file
and then configure it to use AGP's Artifacts
API pulling in the bundle file.
Or you could, for example, get the directory
with all the APKs.
And there's even a way to simply get a list of each APK file
individually.
But we can go even further than that
by using AGP APIs that let us arbitrarily
modify its build artifacts.
So in the example you're seeing here,
we're able to inject ourselves right into AGP's tasks
and modify the manifest after AGP has created it for us
but before it uses it in the next steps of the build.
So AGP has four different APIs for doing arbitrary artifact
modification.
So you have wiredWithFiles, which
is what the manifest transformation task you're
seeing on the right is doing.
So this takes as an input your task provider
and two properties.
One property is supposed to be the input file
and the other property is the output file.
AGP will give you what it has already created to the input
file property and expect you to write out
what you've done to the output file property.
And then it'll use that output file and scooch it
on to the rest of the build.
Then you have wiredWithDirectories,
which does exactly the same thing but for directories.
So you're given an input directory
and expected to produce an output directory.
And then you have wiredWith, which
has a two param version and a one param version.
So the two param version is kind of interesting.
It's like a squishification function.
So you're given a list of files or directories.
And then you're expected to combine all of that
into either one file or one directory.
So where this is useful is, for example,
creating a bundle file.
So you would be given a list of APKs.
And then you would be expected to squishify all of that
into one bundle file.
Finally, we have the most powerful API of them
all-- the single paramed wiredWith.
It simply takes in one output file.
And that means, you can do whatever you want
to create that output file.
So say for example, you want to take over the manifest creation
process.
You'd simply use the single paramed wiredWith method.
And you would just tell AGP, don't do anything
to create the manifest.
I'll take care of it.
And I'll just give it to you once I'm done.
And you can then scooch it along the rest of the build.
And you can do this for other artifacts too,
basically just doing whatever you want to create them.
And yeah, if you want a more detailed description
of all of these methods, check out the description.
All right.
I hope you enjoyed my tip on using
modern AGP and Gradle APIs to manipulate
your Android build artifacts.
Have fun customizing Gradle and AGP to your needs.
Bye.
[MUSIC PLAYING]