The issue I have is that if I try to run my app on different devices that have different Android version I have to clean every time before changing Android versions. If I don't clean, I get the following error:
Zip file '/long path to app/app/build/outputs/app/apk/my_app_name-my_variant_name-verionName.apk' already contains entry 'some file.dex', cannot overwrite
After having this error for a long time I finally found the piece of code that is causing the problem when Android Studio Arctic Fox started telling me exactly which file had the conflict.
The gradle code that causes the problem is this:
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
outputFileName = new File(path + "my_app_name-${variant.name}-${variant.versionName}.apk")
}
}
One solution I've found that saves me a clean is to delete the build/outputs/app directory.
So is there some way to accomplish the same thing without having this issue?
This isn't exactly an answer as to why it happens. As someone pointed out, it might have to do with Android Studio trying to build apks per architecture, however the solution I found here https://stackoverflow.com/a/28992851/704836 does not make different apks per architecture, so I don't know why this fixes it but it does.
Basically I removed the code from the original post:
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
outputFileName = new File(path + "my_app_name-${variant.name}-${variant.versionName}.apk")
}
}
And replaced it with:
setProperty("archivesBaseName", "MyApp-$versionName")
And that was enough to solve the issue. I can not switch phone targets and it works just fine. The new apk that is generated is called MyApp-version-variant.apk
I have to add the Analytics tool Sentry to our Android project. In order to make it work, one needs to create mappings for the obfuscated code (from Proguard/R8) and upload it later to Sentry.
On the website https://docs.sentry.io/platforms/android/ it is even described how to do that.
There it is written that one needs to create a gradle task looking like this:
gradle.projectsEvaluated {
android.applicationVariants.each { variant ->
def variantName = variant.name.capitalize();
def proguardTask = project.tasks.findByName(
"transformClassesAndResourcesWithProguardFor${variantName}")
def dexTask = project.tasks.findByName(
"transformClassesWithDexFor${variantName}")
def task = project.tasks.create(
name: "processSentryProguardFor${variantName}",
type: Exec) {
workingDir project.rootDir
commandLine *[
"sentry-cli",
"upload-proguard",
"--write-properties",
"${project.rootDir.toPath()}/app/build/intermediates/assets" +
"/${variant.dirName}/sentry-debug-meta.properties",
variant.getMappingFile(),
"--no-upload"
]
}
dexTask.dependsOn task
task.dependsOn proguardTask
}
}
This shall wait until Proguard is finished, than copy this properties file to the assets. However, when I add this to my Android gradle script I get the error:
Could not create task
':app:processSentryProguardForPlayStoreStagingDebug'.
No signature of method: java.util.ArrayList.multiply() is applicable for argument types: (ArrayList) values: [[sentry-cli, upload-proguard,
--write-properties, {Application-Path}/app/build/intermediates/assets/playStoreStaging/debug/sentry-debug-meta.properties,
...]] Possible solutions: multiply(java.lang.Number),
multiply(java.lang.Number)
I assume there is something wrong with the multiplication symbol * before the commandLine array. But when I remove it I get the error
Could not create task
':app:processSentryProguardForPlayStoreStagingDebug'.
Cannot cast object 'sentry-cli' with class 'java.lang.String' to class 'int'
So I tried to test this with only that line
commandLine "sentry-cli", ...
Which gave me another error
What went wrong: Cannot invoke method dependsOn() on null object
Thus I assume something went really wrong with that gradle script since it seems the dependend task can't be found. Does anyone have any idea how to fix this (or optionally have any other idea how to copy that sentry-debug-meta.properties file to my assets in another way, once Proguard/R8 is finished)?
Thanks!
-------- EDIT --------
I noticed something important.
The gradle tasks are defined in a different name than what was defined in the manual. Looking at my tasks I have them named
transformClassesAndResourcesWithR8For...
and
transformClassesWithDexBuilderFor...
However, I print the variantName then for checking but it seems my tasks are incomplete.
In my tasks list there exist
transformClassesAndResourcesWithR8ForPlayStoreStagingDebug
but not
transformClassesAndResourcesWithR8ForPlayStoreStagingRelease
and thus the task can't be found. I think that is the real problem here. So where are these gradle tasks defined?
------- EDIT 2 --------
Okay I noticed something strange here. Some variants don't have tasks. It makes sense that DEBUG tasks don't have R8 tasks but I found this here:
Variant: PlayStoreStagingRelease DexTask is null
Variant: PlayStorePreviewRelease DexTask is null
Variant: HockeyAppRelease DexTask is null
Variant: LocalServerRelease DexTask is null
Variant: PlayStoreProductionRelease DexTask is null
So how can this be?
I'd recommend using the Sentry Gradle integration (Gradle plugin) which is described here https://docs.sentry.io/platforms/android/#gradle-integration
The official Android Gradle plugin changed its task names over versions, Gradle version also affects those code snippets.
Google also replaced Proguard with R8 and it also affected those code snippets.
Is there a reason why not using the Sentry Gradle integration? if so, We'll be looking into updating them.
Thanks.
java.util.ArrayList.multiply() hints for that * in front of the [ ] list, which looks strange to me. Try removing the *[ ], only keeping List<String> (there's no ArrayList expected, to begin with):
commandLine "sentry-cli", "upload-proguard", "--write-properties", "${project.rootDir.toPath()}/app/build/intermediates/assets/${variant.dirName}/sentry-debug-meta.properties", variant.getMappingFile(), "--no-upload"
You'd have to look up how your tasks are actually being called, but it should be something alike:
def r8Task = project.tasks.findByName("transformClassesAndResourcesWithR8For${variantName}")
def d8Task = project.tasks.findByName("transformClassesWithDexBuilderFor${variantName}")
With a null check, because not every variant might have minifyEnabled true set:
if(r8Task != null) {
d8Task.dependsOn task
task.dependsOn r8Task
}
Maybe even a previous null check is required, because variant.getMappingFile() needs R8.
And that some flavors have no D8 task might be based upon the absence of code (nothing to do).
Here's a summary of the steps that I followed for integrating Sentry with my Android app. These steps are to ensure the sentry gradle plugin works as expected and automatically uploads the proguard mapping files, without you having to worry about uploading using cli. I assume you would have setup the Sentry SDK as described here:
https://docs.sentry.io/platforms/android/#integrating-the-sdk
Ensure you have Android Studio gradle plugin 3.5.0 (Not 3.6.x, that seems to break the sentry plugin. I observed that the sentry proguard or native symbol upload tasks are not configured or executed at all). This value should be in your root project's build.gradle in dependencies block
Provide a sentry.properties file the root folder of your project. The sentry.properties file should have the following values at minimum:
defaults.project=your_sentry_project_name
defaults.org=your_sentry_org_name
auth.token=sentry_project_auth_token
You can get info about generating auth tokens here: https://sentry.io/settings/account/api/auth-tokens/
(Optional: If you have build flavors) In my case, I have different flavors for my app. So, I had to put the sentry.properties inside my flavor specific folder in /app/src/ folder. Then, I wrote a gradle task to copy the flavor specific sentry.properties file into the project's root folder during gradle's configuration phase. Example:
task copySentryPropertiesTask {
if (getBuildFlavor() != null && !getBuildFlavor().isEmpty()) {
println("Copying Sentry properties file: ${getBuildFlavor()}")
copy {
from "src/${getBuildFlavor()}/"
include "sentry.properties"
into "../"
}
}
}
def getBuildFlavor() {
Gradle gradle = getGradle()
String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
Pattern pattern;
if (tskReqStr.contains("assemble"))
pattern = Pattern.compile("assemble(\\w+)(Release|Debug)")
else
pattern = Pattern.compile("generate(\\w+)(Release|Debug)")
Matcher matcher = pattern.matcher(tskReqStr)
if (matcher.find())
return matcher.group(1)
else {
println "NO MATCH FOUND"
return ""
}
}
Note 1: You can place this task in your app/build.gradle anywhere (I had placed it at the end).
Note 2: If you followed step 3 for build flavors, you can also add the root folder's sentry.properties in .gitignore. Since, it will be copied everytime you create a build.
Sentry should now be able to upload the proguard files for any release builds (or if you set minifyEnabled=true for any buildType).
Gradle sync failed:
Manifest Tasks does not support the manifestOutputFile property any more, please use the manifestOutputDirectory instead.
For more information, please check
https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html
I opened the link and applied it but still can not solve my problem ..
it was said to add this code in build.gradle module app ..
applicationVariants.all { variant ->
variant.outputs.all { output ->
output.processManifest.doLast {
// Stores the path to the maifest.
String manifestPath = "$manifestOutputDirectory/AndroidManifest.xml"
// Stores the contents of the manifest.
def manifestContent = file(manifestPath).getText()
// Changes the version code in the stored text.
manifestContent = manifestContent.replace('android:versionCode="1"',
String.format('android:versionCode="%s"', generatedCode))
// Overwrites the manifest with the new text.
file(manifestPath).write(manifestContent)
}
}
}
I may apply it in wrong way because no resources that cover this topic.
I also try this answer .. Error:Manifest Tasks does not support the manifestOutputFile property any more, please use the manifestOutputDirectory instead. (Android Studio 3.0)
for all my trials the error still exist ..
When trying to enable multi-dexing for an application over 65K, following the guidelines specified by the android developer page here
While in my application build.gradle I specify:
android.applicationVariants.all{
variant -> variant.outputs.each {
output ->
output.processResources.manifestFile = file('AndroidManifest.xml')
output.processManifest.enabled=false
}
}
Upon running build I receive this error,
Error: A problem was found with the configuration of task ':myapp:collectDebugMultiDexComponents'.
File 'C:\Android Projects\myappapp\build\intermediates\manifests\full\debug\AndroidManifest.xml' specified for property 'manifest' does not exist.
Open to any suggestions or advice to properly direct multi dex task to actual AndroidManifest.xml path. When removing the library that causes the application to cross the 64k limit and removing multidexing the application builds fine.
1- Why is the following:
android.buildVariants.each { variant ->
... my code
}
giving me the following error?
Could not find property 'buildVariants' on com.android.build.gradle.AppExtension_Decorated#1bf6bde6.
2- Why is the following silently not executing "... my code"?
android.applicationVariants.each { variant ->
... my code
}
The Android Build System now uses "applicationVariants" instead of "buildVariants". However, "android.applicationVariants.each" will also not work, see below.
Since the Android Build System 0.5.5 release you must use "android.applicationVariants.all" instead of "android.applicationVariants.each", as applicationVariants will remain empty with a call to each.