The following is the first few sections of a chapter from The Busy Coder's Guide to Android Development, plus headings for the remaining major sections, to give you an idea about the content of the chapter.
One common thing to do in a Gradle plugin is to generate Java code. Code that writes other code based upon supplied inputs is referred to as a “code generator”.
At its core, Java code is just text. Developers generate text on the fly quite a bit, particularly in Web development. There are two main approaches to such text generation:
In this chapter, we will examine what it takes to generate Java code
from a Gradle plugin. In particular, we will look at the second
approach, where we will have Java code that generates other Java code,
using a library that makes this a lot easier than writing a lot
append() calls to a
Understanding this chapter requires that you have read the preceding chapter and all of its prerequisites.
You could generate code based off of random numbers. More likely, there is some sort of input that you are using to determine the code to be generated. This input can be divided roughly into three areas: other Java code, other project files, and everything else.
Modern Android code gets littered with annotations. Some are from
standard Java (e.g.,
@Override). Some are from Android libraries
@NotNull). Some are from third-party libraries, like
greenrobot’s EventBus (e.g.,
Sometimes, these annotations are for validation at compile time.
@Override tells Java compilers that we think that we are overriding
a method from a superclass or are implementing an interface method, and
so the compile should fail if our method signature does not match
anything that we could be overriding.
@NotNull indicates that a
parameter should not be
null, allowing static code analysis to help
point out places where we might accidentally pass
null in as a value.
Sometimes, these annotations are used by third-party libraries
at runtime. greenrobot’s EventBus, for example, will look for
annotations to determine how to deliver events to registered event-handling
Sometimes, these runtime annotations generate something resembling code at runtime. Retrofit, for example, creates an instance of our service interface at runtime, with an implementation that will make the Web service requests that we desire. However, this Java bytecode is generated on devices, not as ordinary Java source files that are included in the build.
But, sometimes, these annotations get used by compile-time annotation
processors. These add-ons to the build process read in Java code, analyze
it (annotations in particular), and code generate Java code in support
of our existing Java code. Google’s AutoValue
processor, for example, looks for
classes and code-generates a concrete implementation of a “value class”
(one with immutable members), complete with
and related methods.
A code generator might generate Java code from other types of input that are in the project:
<layout>element) and code-generates Java binding classes for them
SQLiteOpenHelpersubclass based upon a SQL script that you include in your module
Here, the inputs are resources or other files, not annotated Java code.
There is nothing stopping you from generating code based on inputs that come from outside of the project. For example, whereas SQLDelight works off of SQL scripts in your project tree, you can imagine a similar code generator that retrieved schemas from a live database using database I/O.
The big advantage of limiting code generation to files in the project is having reproducible builds based on version control. Ideally, years from now, you should be able to check out a branch or tag from a version control system and be able to build the project the same way then as you do on the day you committed that code to version control. That works best when the entire project specification is included in version control. A server is not, and the server response in a few years might differ from what the server response is today.
One workaround for this is to have two tools. One retrieves the data from the server and writes that data out into files that go into your project (e.g., JSON files). You would run this code from time to time, when you specifically need to get the latest configuration from the server. However, the second tool performs the code generation, working off of the project files, not from the live server data. That way, you can perform that code generation again in the future in a reproducible fashion.
The preview of this section is in an invisible, microscopic font.
The preview of this section was traded for a bag of magic beans.
The preview of this section is in the process of being translated from its native Klingon.