Mobile Dev

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.

4 answers 3 views

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:

groovy
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:

groovy
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

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:

groovy
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+):

groovy
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:

groovy
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:

bash
./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)

groovy
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

groovy
android {
 packagingOptions {
 resources.merges.add("META-INF/LICENSE")
 resources.merges.add("META-INF/AL2.0")
 }
}

For Gradle 4.0 to 6.8 (Older versions)

groovy
android {
 packagingOptions {
 merge "META-INF/LICENSE"
 merge "META-INF/NOTICE"
 merge "META-INF/DEPENDENCIES"
 }
}

For Gradle 3.x (Very old versions)

groovy
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:

groovy
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:

groovy
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:

groovy
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:

groovy
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:

groovy
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:

bash
./gradlew clean

Step 4: Rebuild Your Project

Now rebuild your project:

bash
./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:

  1. Updating your Android Gradle Plugin to the latest version
  2. Updating all your dependencies to their latest stable versions
  3. 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

  1. 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

  2. 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/

  3. 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.

S

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+:

groovy
packagingOptions {
 resources.merges.add("META-INF/LICENSE")
 resources.merges.add("META-INF/AL2.0")
 resources.merges.add("META-INF/LGPL2.1")
}

For older versions:

groovy
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.

Stephan Petzl / Software Engineer and Test Automation Expert

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+:

groovy
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:

groovy
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.

Authors
S
Senior Developer
A
Software Engineer
S
Software Engineer
Stephan Petzl / Software Engineer and Test Automation Expert
Software Engineer and Test Automation Expert
Sources
Documentation Portal
Verified by moderation
Moderation
Android Gradle Build Error: Fix META-INF/LICENSE Without Breaking LeakCanary