Android Gradle Build Error: Fix META-INF/LICENSE Without Breaking LeakCanary
Resolve Android Gradle build errors with duplicate META-INF/LICENSE files using merge instead of exclude. Preserve LeakCanary functionality while fixing build conflicts.
Android Gradle build error: More than one file found with OS independent path ‘META-INF/LICENSE’ – How to resolve without breaking LeakCanary?
When building my Android app, I get this error:
Error: Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'.
More than one file was found with OS independent path 'META-INF/LICENSE'
Here is my app/build.gradle file:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
defaultConfig {
applicationId "cn.sz.cyrus.kotlintest"
minSdkVersion 14
targetSdkVersion 25
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
javaCompileOptions{
annotationProcessorOptions{
includeCompileClasspath = true
}
}
multiDexEnabled true
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
packagingOptions {
/*
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
*/
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
compile "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
compile 'com.android.support:appcompat-v7:25.3.1'
testCompile 'junit:junit:4.12'
compile 'com.android.support:design:25.3.1'
compile 'com.android.support.constraint:constraint-layout:1.0.2'
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
compile 'com.github.GrenderG:Toasty:1.2.5'
compile 'com.orhanobut:logger:1.15'
compile 'io.reactivex.rxjava2:rxandroid:2.0.1'
compile 'com.umeng.analytics:analytics:latest.integration'
compile 'ai.api:libai:1.4.8'
compile 'ai.api:sdk:2.0.5@aar'
// api.ai SDK dependencies
compile 'com.google.code.gson:gson:2.8.0'
compile 'commons-io:commons-io:2.4'
compile 'com.android.support:multidex:1.0.1'
}
Uncommenting and adding these packagingOptions excludes resolves the duplicate file error:
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
However, the app then crashes at runtime with:
java.lang.NoClassDefFoundError: com.squareup.leakcanary.internal.HeapAnalyzerService
at com.squareup.leakcanary.LeakCanary.isInAnalyzerProcess(LeakCanary.java:145)
at cn.sz.cyrus.wemz.TestApplication.onCreate(TestApplication.kt:32)
How can I fix the duplicate META-INF/LICENSE files during the build without causing the LeakCanary NoClassDefFoundError?
Android Gradle build errors with duplicate META-INF/LICENSE files commonly occur when multiple libraries include the same license file, causing conflicts during the build process. The solution is to use the merge approach in packagingOptions instead of excluding these files, which preserves necessary files that libraries like LeakCanary depend on for proper runtime functionality.
Contents
- Understanding the Android Gradle Build Error with META-INF/LICENSE
- Why Excluding META-INF Files Breaks LeakCanary
- Using Merge Instead of Exclude to Preserve LeakCanary Functionality
- Gradle Version-Specific Solutions for META-INF Conflicts
- Alternative Approaches to Resolve Duplicate File Conflicts
- Step-by-Step Implementation Guide
Understanding the Android Gradle Build Error with META-INF/LICENSE
The “More than one file was found with OS independent path ‘META-INF/LICENSE’” error is a common Android Gradle build problem that occurs when multiple dependencies in your project include the same META-INF/LICENSE file. This happens because many libraries include their license files in the META-INF directory, and when these are packaged into your APK, Gradle encounters duplicate files with identical paths.
The error specifically appears during the transformResourcesWithMergeJavaResForDebug task in your build process. This task is responsible for merging Java resources from all dependencies into your final APK. When duplicate files are found with the same path across different libraries, Gradle doesn’t know which one to keep, causing the build to fail.
Android Studio reports this error clearly:
Error: Execution failed for task ':app:transformResourcesWithMergeJavaResForDebug'.
More than one file was found with OS independent path 'META-INF/LICENSE'
This typically happens when you have dependencies like LeakCanary, Gson, Commons IO, or other libraries that each include their own META-INF/LICENSE files in their JAR archives.
Why Excluding META-INF Files Breaks LeakCanary
When you exclude META-INF files like LICENSE as shown in your configuration:
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
exclude 'META-INF/NOTICE'
exclude 'META-INF/LICENSE'
exclude 'META-INF/LICENSE.txt'
exclude 'META-INF/NOTICE.txt'
}
You’re telling Gradle to remove these files from your APK entirely. While this resolves the duplicate file conflict during build time, it can cause runtime issues because some libraries, including LeakCanary, actually depend on certain META-INF files for proper functioning.
In your case, excluding META-INF/LICENSE files causes a NoClassDefFoundError for com.squareup.leakcanary.internal.HeapAnalyzerService. This happens because LeakCanary’s internal classes rely on license files or other resources that were excluded from the APK during the build process.
The stack trace you’re seeing confirms this issue:
java.lang.NoClassDefFoundError: com.squareup.leakcanary.internal.HeapAnalyzerService
at com.squareup.leakcanary.LeakCanary.isInAnalyzerProcess(LeakCanary.java:145)
at cn.sz.cyrus.wemz.TestApplication.onCreate(TestApplication.kt:32)
This error occurs because the necessary classes aren’t properly included in your APK when you exclude these META-INF files. The solution isn’t to exclude the files but to merge them properly.
Using Merge Instead of Exclude to Preserve LeakCanary Functionality
The correct approach to resolve duplicate META-INF files while preserving library functionality is to use the merge directive instead of exclude in your packagingOptions. This tells Gradle to combine files with the same path from different dependencies rather than removing them entirely.
Here’s how you should modify your app/build.gradle file:
For modern Gradle versions (7.0+):
android {
// ... other configurations ...
packagingOptions {
resources.merges.add("META-INF/LICENSE")
resources.merges.add("META-INF/AL2.0")
resources.merges.add("META-INF/LGPL2.1")
}
}
For older Gradle versions:
android {
// ... other configurations ...
packagingOptions {
merge "META-INF/LICENSE"
merge "META-INF/AL2.0"
merge "META-INF/LGPL2.1"
}
}
This approach preserves the content from all dependencies while resolving the duplicate file issue. The merge directive tells Gradle to combine the contents of files with the same path, which is especially important for license files that multiple libraries include.
As noted in the Android Gradle DSL documentation, the merge approach is more “legally safe” than exclude functions as it preserves license information from all dependencies.
After making these changes, you’ll need to clean and rebuild your project using:
./gradlew clean ./gradlew build
This should resolve the build error while maintaining LeakCanary’s functionality.
Gradle Version-Specific Solutions for META-INF Conflicts
Different versions of Android Gradle Plugin require slightly different syntax for the merge directive. Here are solutions for various Gradle versions you might be using:
For Gradle 7.2+ (Latest versions)
android {
packagingOptions {
resources.merges.add("META-INF/LICENSE")
resources.merges.add("META-INF/AL2.0")
resources.merges.add("META-INF/LGPL2.1")
}
}
For Gradle 7.0 to 7.1
android {
packagingOptions {
resources.merges.add("META-INF/LICENSE")
resources.merges.add("META-INF/AL2.0")
}
}
For Gradle 4.0 to 6.8 (Older versions)
android {
packagingOptions {
merge "META-INF/LICENSE"
merge "META-INF/NOTICE"
merge "META-INF/DEPENDENCIES"
}
}
For Gradle 3.x (Very old versions)
android {
packagingOptions {
pickFirst 'META-INF/LICENSE'
pickFirst 'META-INF/NOTICE'
}
}
Note that the pickFirst approach is less ideal than merge as it arbitrarily chooses one file over others, potentially losing important information from other libraries. The merge approach is preferred when available.
If you’re still encountering issues after trying these solutions, you might need to update your Android Gradle Plugin to a more recent version that supports the resources.merges.add() syntax.
Alternative Approaches to Resolve Duplicate File Conflicts
While the merge approach is generally the best solution for META-INF conflicts, here are some alternative approaches you can try if you’re still experiencing issues:
1. Dependency Exclusion with Precision
Instead of excluding all META-INF files, you can exclude them from specific dependencies that aren’t critical:
dependencies {
compile('com.some.library:some-library:1.0.0') {
exclude group: 'org.license', module: 'license-file'
}
}
2. Force a Single Version of Problematic Dependencies
Sometimes the conflict arises from having multiple versions of the same library. You can force a specific version:
configurations.all {
resolutionStrategy {
force 'com.google.code.gson:gson:2.8.0'
}
}
3. Use Gradle’s Exclude Rule in the Configuration
You can also try excluding the files directly in the configuration:
configurations {
all*.exclude group: 'group.name', module: 'module.name'
}
4. Upgrade Dependencies
Sometimes updating to newer versions of your dependencies resolves these conflicts, as library maintainers often address these issues in newer releases.
5. Use the Android Gradle Plugin’s AAPT2 Options
For newer Android Gradle Plugin versions, you can also try:
android {
aaptOptions {
additionalParameters "--enable-splits-in-apk"
}
}
However, the merge approach remains the most reliable solution for preserving library functionality while resolving duplicate file conflicts, especially for critical libraries like LeakCanary.
Step-by-Step Implementation Guide
Here’s a complete step-by-step guide to resolve your META-INF/LICENSE conflict while preserving LeakCanary functionality:
Step 1: Backup Your Build Configuration
Before making changes, backup your current app/build.gradle file to avoid losing any important configurations.
Step 2: Modify the packagingOptions Section
Replace your current packagingOptions with the merge approach:
android {
// ... keep all your existing configurations ...
packagingOptions {
resources.merges.add("META-INF/LICENSE")
resources.merges.add("META-INF/AL2.0")
resources.merges.add("META-INF/LGPL2.1")
// You can also add other common META-INF files if needed:
// resources.merges.add("META-INF/NOTICE")
// resources.merges.add("META-INF/DEPENDENCIES")
}
}
Step 3: Clean Your Project
Execute a clean build to ensure all previous build artifacts are removed:
./gradlew clean
Step 4: Rebuild Your Project
Now rebuild your project:
./gradlew build
Step 5: Verify the Build
Check that the build completes successfully without the META-INF/LICENSE error.
Step 6: Test LeakCanary Functionality
Run your app and verify that LeakCanary is working properly. You should see the LeakCanary icon appear in your app, and it should start monitoring for memory leaks.
Step 7: Verify in Debug Mode
Since you have LeakCanary configured only for debug builds (debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'), make sure to test specifically in debug mode to confirm functionality.
Step 8: If Issues Persist
If you still encounter issues, try:
- Updating your Android Gradle Plugin to the latest version
- Updating all your dependencies to their latest stable versions
- Checking for any other META-INF conflicts in your build output
This approach should resolve your build error while maintaining LeakCanary’s memory leak detection capabilities, which are crucial for developing stable Android applications.
Sources
-
Stack Overflow Answer - Detailed solution for META-INF/LICENSE conflicts with code examples for different Gradle versions: https://stackoverflow.com/questions/44342455/more-than-one-file-was-found-with-os-independent-path-meta-inf-license
-
Repeato Android Build Error Guide - Step-by-step instructions for resolving duplicate file conflicts while preserving library functionality: https://www.repeato.app/resolving-the-more-than-one-file-was-found-with-os-independent-path-meta-inf-license-error-in-android-studio/
-
Google Android Gradle DSL Documentation - Official documentation on packaging options and merge functionality: https://google.github.io/android-gradle-dsl/3.2/com.android.build.gradle.internal.dsl.PackagingOptions.html
Conclusion
Resolving Android Gradle build errors with duplicate META-INF/LICENSE files requires a careful approach that maintains library functionality. The merge directive in packagingOptions is the optimal solution, as it resolves conflicts while preserving necessary files that libraries like LeakCanary depend on for proper runtime behavior. By using resources.merges.add() or the older merge syntax depending on your Gradle version, you can successfully build your application without breaking critical debugging tools. Remember to clean and rebuild your project after making changes, and verify that LeakCanary functions correctly in debug mode to ensure you maintain memory leak detection capabilities in your Android development workflow.
You can resolve the META-INF/LICENSE conflict by using the merge approach instead of excluding files. Add this to your app/build.gradle inside the android block:
For Gradle 7.2+:
packagingOptions {
resources.merges.add("META-INF/LICENSE")
resources.merges.add("META-INF/AL2.0")
resources.merges.add("META-INF/LGPL2.1")
}
For older versions:
packagingOptions {
merge "META-INF/LICENSE"
merge "META-INF/AL2.0"
merge "META-INF/LGPL2.1"
}
This preserves necessary files while resolving conflicts, unlike which can break LeakCanary by removing files it depends on at runtime. After making changes, clean and rebuild your project using ./gradlew clean and ./gradlew build.
When encountering the “More than one file was found with OS independent path ‘META-INF/LICENSE’” error during Android app building, the solution is to use the merge approach in your packagingOptions. This resolves the duplicate file issue while preserving necessary files that libraries like LeakCanary depend on. Add this to your build.gradle:
For Gradle 7.0.2+:
android {
packagingOptions {
resources.merges.add("META-INF/LICENSE")
}
}
This approach is more “legally safe” than exclude functions as it preserves license information from all dependencies. After implementing, remember to clean and rebuild your project to apply the changes.
The Android Gradle DSL documentation provides an example of merging LICENSE.txt files in the root directory. For the META-INF/LICENSE conflict, use the merge approach which takes precedence over excludes in Gradle’s packaging options resolution:
packagingOptions {
merge "/META-INF/LICENSE"
// Same as: merges += ["/META-INF/LICENSE"]
}
This method preserves necessary files while resolving conflicts, ensuring libraries like LeakCanary continue to function properly at runtime. The merge approach is particularly valuable when dealing with license files that multiple dependencies include.