Android Gradle Build Plugin 4.0.0 & R8 Desugaring Issues on API 19
This article dives into the challenges faced when using Android Gradle Build Plugin 4.0.0 and R8 desugaring on Android devices running API level 19 (KitKat).
Understanding the Issue
Android Gradle Build Plugin 4.0.0 introduced R8 as the default code shrinking and obfuscation tool, replacing ProGuard. While R8 offers several improvements, it introduces a compatibility issue with API 19, leading to runtime crashes and unexpected behavior. This stems from the way R8 handles desugaring – the process of converting Java language features introduced in newer Android versions (like Java 8 language features) to be compatible with older API levels.
Causes
- Limited Java 8 Language Feature Support in API 19: Android API 19 doesn’t natively support all Java 8 language features that newer Android versions do. This creates compatibility issues when R8 attempts to desugar code.
- R8’s Desugaring Limitations on API 19: While R8 is designed to handle desugaring, its effectiveness on API 19 is limited. Certain desugaring scenarios might not work as expected, leading to runtime errors.
Workarounds and Solutions
While a complete fix might not be immediately available, there are workarounds and solutions you can implement:
1. Disable Desugaring
If possible, the most straightforward solution is to disable desugaring for API 19 specifically. This can be done by modifying your `build.gradle` file:
android { compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } buildFeatures { viewBinding true } buildTypes { debug { minifyEnabled false shrinkResources false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } release { minifyEnabled true shrinkResources true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileSdkVersion 30 buildToolsVersion '30.0.2' defaultConfig { applicationId 'com.example.android' minSdkVersion 19 targetSdkVersion 30 versionCode 1 versionName "1.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } flavorDimensions "version" productFlavors { develop { dimension "version" versionNameSuffix "-dev" applicationIdSuffix ".dev" } release { dimension "version" versionNameSuffix "-release" applicationIdSuffix ".release" } } packagingOptions { pickFirst 'lib/armeabi-v7a/libjsc.so' pickFirst 'lib/arm64-v8a/libjsc.so' pickFirst 'lib/x86/libjsc.so' pickFirst 'lib/x86_64/libjsc.so' } } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'com.google.android.material:material:1.2.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.navigation:navigation-fragment:2.3.3' implementation 'androidx.navigation:navigation-ui:2.3.3' implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.0' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0' implementation 'androidx.legacy:legacy-support-v4:1.0.0' implementation 'com.google.firebase:firebase-analytics:19.0.0' testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' implementation platform('com.google.firebase:firebase-bom:28.4.0') implementation 'com.google.firebase:firebase-auth-ktx' implementation 'com.google.firebase:firebase-firestore-ktx' implementation 'com.google.firebase:firebase-database-ktx' implementation 'com.google.firebase:firebase-storage-ktx' implementation 'androidx.core:core-ktx:1.6.0' implementation 'androidx.recyclerview:recyclerview:1.2.1' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' implementation 'com.squareup.okhttp3:logging-interceptor:4.9.1' implementation 'com.github.bumptech.glide:glide:4.12.0' annotationProcessor 'com.github.bumptech.glide:glide-compiler:4.12.0' implementation 'com.google.code.gson:gson:2.8.6' }
This configures the `minSdkVersion` to 19 and applies the following settings:
- `minifyEnabled`: Set to `true` to enable code shrinking and obfuscation.
- `shrinkResources`: Set to `true` to enable resource shrinking.
- `proguardFiles`: Specifies the ProGuard configuration files to be used for code shrinking and obfuscation.
2. Use Older Gradle Plugin Version
If possible, consider reverting to an earlier version of the Android Gradle Build Plugin (e.g., 3.x). Older versions might have better compatibility with API 19 in terms of R8 desugaring. However, this approach may limit access to newer features available in later plugin versions. You can set this in your `build.gradle` file using the `classpath` option:
dependencies { classpath 'com.android.tools.build:gradle:3.5.0' // Example older plugin version }
3. Minimize Java 8 Usage
Try to minimize the use of Java 8 language features in your codebase if possible. This can reduce the burden on R8’s desugaring process and improve compatibility with API 19. For instance, consider using simpler alternatives to lambdas or streams where feasible.
4. Targeted Bug Fixes
If you’re experiencing specific runtime errors related to R8 desugaring, consider searching for relevant bug reports on the Android issue tracker or other developer communities. There might be known issues and potential workarounds that have been discovered.
Conclusion
While Android Gradle Build Plugin 4.0.0 and R8 offer significant benefits, compatibility with older API levels like API 19 can pose challenges. By understanding the root cause of desugaring issues and implementing the provided workarounds, you can mitigate these problems and maintain app functionality on older devices.