February 2020 was quite busy as we got 4.1 Canary release of Android Studio and Android Gradle Plugin (AGP), former 4.0 Canary was bumped to Beta and we got Stable version of 3.6.
Android Gradle Plugin 3.6 is the last version in 3.x series and the last version that will work with Gradle 5.x. In tradition of minor version updates, it brings few smaller enhancements, but this is time it has also a new Feature with big F.
View Binding (docs)
What is the most famous function on Android?
Probably View.findViewById()
. For various reasons developers are trying to avoid using it as much as possible.
There is a complete history of 3rd party libraries and frameworks like Kotlin Android Extensions, Butter Knife or Android Annotations that are trying to hide it behind a nicer API or annotations.
Also Google contributed to this list with Data Binding that also might serve as a replacement for binding View
s defined in layout.xml
and i.e. Fragment
's code, although it is maybe too heavy to be used for binding views only.
Maybe this was the reason why a new Google tool called View Binding
was created.
How does it work?
I won't go into details about what it does and how you can use it as we have already a plenty of other blog posts out there.
But there is one technical detail I didn't find anywhere. From the docs:
Once view binding is enabled in a module, it generates a binding class for each XML layout file present in that module.
But who generates the code?
- It cannot be an Annotation Processor as in comparison to Data Binding, View Binding is said to require no annotation processing for faster compilation.
- It cannot be Kotlin Compiler Plugin as View Binding works also for Java code.
I cannot find the answer anywhere, so I asked the author. It turns out that View Binding is a standalone tool that consist of three components.
Code Generator
This is the actual View Binding's engine.
It creates a ViewBinder.kt model for each layout in the project that requires a *Binding
class to be generated.
Then there is ViewBinderGenerateSource.kt that takes the outputs of Data Binding XML layout parser and generates binding code using JavaPoet.
Artifact: androidx.databinding:databinding-compiler-common
(link)
Build Hook
Code generation is invoked by Android Gradle Plugin during build. There is no much difference from how Data Binding is configured in TaskManager.java.
Artifact: com.android.tools.build:gradle
(link)
Feature API
Last piece of the puzzle. View Binding has a tiny public API available to developers that consists of single class: ViewBinding.java. It serves as marker interface for all generated *Binding
classes.
Artifact: androidx.databinding:viewbinding
(link)
So, who generates the code?
Annotation Processors tend to be problematic in terms of configuration and performance.
Kotlin Compiler Plugins tend to be problematic to work with Java code (joke).
View Binding tend to be tiny, simple and quick. It shows us that sometimes there is a better way how to solve a problem out of "standard tools". In this case, it is one's own code generator baked to the Android Gradle Plugin.
Maven Publish plugin support (docs)
Android Gradle Plugin 3.6 is not all about View Binding. If you are on a bigger project where dependencies are not shared as source code dependency but rather as compiled artifact dependency through artifact repository, it might be handy for you that AGP now supports Gradle's built-in Maven Publish plugin.
For most of the projects this might save a few tens lines of Gradle publishing logic.
Enhancements on R class generation
R
class serves as a static list of application resources known at compile time. As projects grow their R
classes might easily get a few hundred thousand or even million lines of code that need to be compiled.
To optimize the build performance AGP 3.6 starts to generate the Java bytecode directly so you don't have compile it. It means that instead of R.java
file in build/generated
directory you can find R.jar
file in build/intermediates
.
Another change is that there is only one R
class generated per library module. This forces you to have a unique package name for each project module. If you need help with this requirement, there is a check introduced in AGP 3.4 that might serve you.
But what is the main reason that R
classes are so big? By default, the module R
class contains references to all module resources but also inherits all references from other module dependencies.
Example: Do you depend on Material Design Components library in your base module? Sorry, all of your project module R
classes will contain few thousand of MDC fields. Bummer!
But there is a cure. AGP 3.3 introduces not very well documented flag. When you enabled it in gradle.properties
:
android.namespacedRClass=true
it prevents references from dependencies being included in your module R
class.
It also makes your module better isolated from architecture point of view as, unless you use explicitly import R
class from other module, you can use only your module's resources. It prevents you to accidentally use a drawable or a string from another module.
New default packaging tool (docs)
It is called zipflinger and you should not care unless you are doing some custom app packaging magic or so.
In such case, or in case you are somehow struggling with the new packaging tool, you can disable it in gradle.properties
:
android.useNewApkCreator=false
Conclusion
So that's it for Android Gradle Plugin 3.x series. Next time let's check what's new in Gradle 6.x and Android Gradle Plugin 4.x !!!
Top comments (0)