What's new in Android Gradle Plugin 3.4
Tags: gradle, android, kotlin, build
Published: 2020-04-04 20:24:39

This post is part of *Gradle for mobile developers* series that highlights Gradle Build System features useful for every developer on mobile project (mainly Android project). To extend this effort I'd like to cover also releases of Android Gradle Plugin (AGP). There are many blog posts presenting new features of Android Studio, but not much presenting the Android Gradle Plugin itself.

I'll start with Android Gradle Plugin 3.4, released in April 2019, as it was the first version that builds on top of Gradle 5.x version only (which correlates with my previous Gradle 5.x series posts).

# R8 enabled by default
R8 is a new tool used in Android build pipeline introduced in Android Gradle Plugin 3.2 as feature preview and now being enabled by default for everyone. It is responsible for:
* *shrinking* = removing unused code and resources, code obfuscation, and optimizations (replaces *ProGuard*)
* *desugaring* = backporting of newer Java version language features to Android Java version (replaces AGP internal desugar tool)
* *dexing* = converting Java bytecode to Dalvik bytecode (replaces *dx*)

R8 is able to do all these steps at once, and comparing to the previous build pipeline, R8 is faster while lowering the APK output size. It saves the time by parsing inputs just once, and creates smaller outputs (APKs or bundles) using broader optimizations. Also, the fact that the entire tool is developed by Google and open-sourced creates a promise of even more features in the future.

If you want to know more about R8, there is no better source then [the R8 blog series by Jake Wharton](https://jakewharton.com/blog).

For some time, there will be an option to switch back to the old build pipeline, but I recommend migrate to R8 as soon as possible.
To switch back, define these flags in `gradle.properties`:
```
  android.enableR8.libraries = false  # Switch to the old pipeline for library modules
  android.enableR8 = false            # Switch to the old pipeline for all modules
```

### Compatibility with ProGuard

R8 holds the most of the configuration and feature compatibility with ProGuard by default. There are some configuration elements ignored, mainly for optimizations not supported by R8. The only problem I struggled with was with *Gson* library. ProGuard has an implicit support for it that R8 does not have, so you need to add few extra rules if you use Gson in project, in my case:
```
  -keepattributes *Annotation*
  -keep class * implements com.google.gson.TypeAdapterFactory
  -keep class * implements com.google.gson.JsonSerializer
  -keep class * implements com.google.gson.JsonDeserializer
  -keep class com.myapp.**Dto { *; } # this keeps all model classes using Gson in app
```

### Full mode

The compatibility with ProGuard is good for current projects migrating to R8, but if you are starting a new project and want to get more from R8, you can enable the *Full Mode*:
```
  android.enableR8.fullMode = true
```
This applies additional optimizations, that can further reduce app size. However, you might need a few extra keep rules to make it work. One such example is *Retrofit* library that actually fixed it internally and publish new keep rules with the library. The only thing you need to do is update Retrofit to version 2.6.0 or newer.


# New lint check dependency configurations ([docs](https://developer.android.com/studio/releases/gradle-plugin#new_features_2))

Lint dependency configurations allow you to consume or publish custom Lint rules. This might be interesting for you in two cases:
1. Your application consumes custom Lint rules defined locally for your project. In the project's `build.gradle.kts` a dependency on module with custom Lint rules is defined by `lintChecks` configuration (actually this was introduced already in Android Gradle Plugin 3.0).
2. Your library publishes custom Lint rules to consumers. In previous versions this was defined by the same `lintChecks` configuration, but in Android Gradle Plugin 3.4 there is a new `lintPublish` configuration created directly for this purpose. It should allow library authors to use one set of custom Lint rules internally to enhance quality of library code, and publish another set of Lint rules for library consumers to enhance its use in consumer applications.
```
  dependencies {
    lintChecks project(':lint-private') // Lint rules to be executed on library code
    lintPublish project(':lint-public') // Lint rules to be executed on consumer application code
  }
```

# Unique Package Names

There is an interesting paragraph in the AGP 3.4 release notes:
> The correct usage of unique package names is currently not enforced but will become more strict on later versions of the plugin. On Android Gradle plugin version 3.4, you can opt-in to check whether your project declares acceptable package names by adding the line below to your `gradle.properties` file:
```
android.uniquePackageNames=true
```

Actually, I didn't find any details for this requirement. But having one package name used only in one project module seems a good thing anyway, so why not enabling this check right now and having no migration issues in the future, right?

# Build Speed Degradation

This is not a feature, rather negative impact observed after migration of few projects to new Android Gradle Plugin version. The build started to be much slower and log was clogged by warnings:
```
  Daemon shut down due to memory pressure
```

The reason in my case was R8 use more memory than ProGuard and once Gradle Daemon hits the memory limit, Java Garbage Collector has been executed very often, thus the speed degradation.

The resolution is simple: give Gradle build more Java Heap space. You can configure it in project's `gradle.properties` file:
```
  org.gradle.jvmargs=-Xmx2g
```

It is not a good practice to set the new memory limit by guess. Gradle build needs enough memory to run smoothly. But if you set the memory limit too high, it might affect how your computer and operation system runs and more memory may actually lead to slower build times.

But there is a tool that helps you to find the proper value, [Gradle Build Scan](https://scans.gradle.com).

With default setting (`-Xmx512m`) the build uses all the memory (and probably needs more). Build Scan output:
![Build Scan output for run with 512m of memory](https://dev-to-uploads.s3.amazonaws.com/i/34wjsypoqz057pt11whw.png)

With adjusted setting (`-Xmx2g`) the build has some memory reserve and runs smoother (and faster). Build Scan output:
![Build Scan output for run with 2g of memory](https://dev-to-uploads.s3.amazonaws.com/i/0lt3878fdupdzssrm56q.png)

On mid-size projects (roughly 200k LoC and 50+ Gradle modules) its fine to set memory limit to 3GB or 4GB.

# Conclusion
Thank you for reading and stay tuned for next Android Gradle Plugin release reviews.