Create your first custom Gradle Plugin for Android — Part 2: generating resources at build-time

Magicbluepenguin
5 min readJun 2, 2020

--

A hands on tutorial to get started with adding custom functionality to your Android builds using Kotlin

While the first part of this series focused on setting up the basic structure of a custom Gradle plugin, we will now move on to turn our work into something useful. The goal for this next instalment will be to create a Task capable of generating resources at build-time, which we will then use in our app.

Once again, if you want to skip all the reading and get straight to the code you can find it all on GitHub.

Picking up where we left off:

In the first part of this write up we created a simple Task whose only purpose was to create a “Hello world” text file in our project directory. What we want to do now is extend this and turn it into something a little more useful. Specifically we want to generate some project resources at build-time, which we can then use in our app. To keep things simple we are going to generate three simple colours, the same process however can be used to create other resource types too.

Let’s start!

Although Gradle will create our colours at build-time, we’ll still need provide their definitions so that we can refer to them in our code. To do this we’ll declare three new entries in our colors.xm file, just as we normally would, with the difference being that this time we will omit their values:

Next, because we will want to see what we have created, let us prepare our Activity to show these colours. Open the main_activity.xml file and add three views, each to display one of the colours:

This part is done. If you now run the app you should see a blank screen. This is as expected because our colours do not yet have value. We’ll remedy that soon enough.

A little cleanup:

Before we get into the nitty gritty of building our Task, let’s do some cleanup. First of all I created a new file called Extensions.kt. In this file I placed the two extensions we had defined in Part 1. Here I also added a simple function to write xml to a file with the necessary tags. The complete Extensions.kt file should look something like this:

Next we’ll actually remove most of what we had originally put in our plugin’s apply method as we will re-define our Task in a standalone class. When done, our plugin class will look like this:

Next we are going to add the new functionality for generating our resources.

Create the new Task:

We had originally declared the sole task of our plugin as a lambda. While this is fine for running quick experiments, it is always good practice to split every task of a plugin into a separate class. The following will take care of our current use-case:

Note the following:

  • The Task class needs to be declared open or Gradle will not be able to register it.
  • Colours are hardcoded for this example. In a real-life project — or in a future instalment of this tutorial 😉 — these would most likely come from a dynamic source, such as a file or a server.
  • The @TaskAction annotation denotes the entry method that Gradle will call when running the task. The method name itself is irrelevant.
  • The @get:Input and @get:OutputFile annotations denote the inputs and outputs for our task. These declarations are optional but highly recommended. Gradle will check these for changes before deciding whether a task needs to be run or not. Only running tasks when needed can help reduce build times in the long run. We will see these at work once we finally run our code.

Now that the task is done we need to register it with the project. Go back to the MyFirstPlugin.kt file and modify it to contain the following:

IMPORTANT: the file structure is key here. We MUST register the root directory for the variant as a generatedResFolder for the project. We MUST however place our resource declarations in a subdirectory of called values — just like with standard Android resources. Failure to do either will make the resources invisible to our app.

To check that the structure is correct delete your app’s build folder, then go ahead and sync Gradle. Once synching is done you should find that the tasks we had added in Part 1 have now been replaced with two new ones:

Double-click on generateColorsDebug, wait for it to complete and then open up the build folder. If everything worked correctly you should see the following folder structure:

If you don’t see this, don’t despair. Go through the code here again and maybe check out the project for this tutorial on Github. Just remember to sync Gradle after you make changes to your code to ensure they’re correctly applied.

The moment of truth:

For the next step I suggest that you again delete the build folder of your app. Trust me, it’s worth it!

After you’ve done that, open activity_main.xml in Android Studio and tap on the design window. This should still look blank, probably something like this:

Now again double click on generateColorsDebug and watch!

Congratulations. You’re now one step closer to total Gradle domination!

One last thing:

As mentioned before, inputs and outputs declarations allow Gradle to determine whether a Task needs to be run or not. If you want to see this in action and have just run the generateColorsDebug task, try to run it one more time and observe the console output. This should now be marked as UP-TO-DATE, meaning that Gradle skipped it:

Now try to modify the Task’s inputs or outputs, for example by adding a new colour to the colorsMap or simply by deleting the build folder again. Double-click on the task once more and you will see that this time it’s no longer marked as UP-TO-DATE, meaning Gradle just run it again.

--

--