I need to save each APK with a special name whenever I build one.
This is the desired name format: "buildType appName_version_Date_Time.apk"
Example: "debug myAppName_v1.0_20161009_0854.apk"
Example: "release myAppName_v1.0_20161009_0854.apk"
For this purpose I added this gradle script:
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
def formattedDate = new Date().format('yyyyMMdd_HHmm')
println(output.outputFile)
def newName = variant.name + " myAppName_v" + defaultConfig.versionName + "_" + formattedDate + ".apk"
output.outputFile = new File(output.outputFile.parent, newName)
}
}
}
This script works, but android studio couldn't find the renamed APK. I also tried clean/rebuild project but it ocures again.
Android stuio error:
The APK file path\to\project\app\build\outputs\apk\debug myAppName_v1.0_[20161009_0854].apk does not exist on disk.
Error while Installing APK
When I check "build\outputs\apk" directory i see that an APK has been built and its name is "myAppName_v1.0_[20161009_0856].apk".
What's wrong here??
Instead of including the current time (with minutes, no less) in the APK filename, consider the following approaches:
Take the timestamp of the latest version control commit (that stays stable)
Instead of changing the apk filename, copy it from it's non-timestamped name - that Android Studio will easily find - to a timestamped apk.
Here's an example to try (haven't run it but should give the general idea). Choose either Files.copy or the copy task, not both.
applicationVariants.all { variant ->
variant.outputs.each { output ->
def copyApkTask = tasks.create(name: "copy" + variant.name + "Apk") {
def newName = ... // can include the timestamp with minutes
println(newName)
// Using java.nio.Files.copy
def targetPath = new File(output.outputFile.parent, newName).toPath()
Files.copy(output.outputFile.toPath(), targetPath)
// Using gradle's Copy logic (clunky for single-file copy and rename)
copy {
from output.outputFile
into output.outputFile.parent
rename { String fileName ->
newName
}
}
}
copyApkTask.mustRunAfter variant.assemble
}
}
you are telling gradle to include the minute in the name of your apk.
then it builds it with the name you want in the n minute, and when it wants to refer to it in the n+1 minute it cant find the file. so it encounters this error.
you better use the commit hash or the date in your apk name. (not including small units like minute or second or even hour)
Related
I need to set up a custom task that I run after an APK is built when using Gradle for Android. Right now I actually have two tasks. One builds an XML file using the path to the APK as an input, and the other runs after the XML task (named packageConfig) and bundles the APK and the XML file into a ZIP.
Right now, I'm having trouble with my packageConfig task. It runs the python script for only the "fullRelease" product flavor, when I actually have multiple:
x86Debug
x86Release
armDebug
armRelease
fullDebug
fullRelease
My script is only executed once, and for the wrong APK. For example, I will invoke gradle like so:
$ gradlew :Applications:zApp:assemblex86debug packageConfig
I set up a print() call in my python script to output the APK path passed in, and it is:
build\outputs\apk\full\release\zApp.apk
The path I'm expecting (based on the product flavor I chose on the command line, as well as the APK I know that is there):
build\outputs\apk\x86\debug\zApp.apk
I'll paste my gradle task below. Can anyone help me understand what I'm doing wrong? What corrections can I make to my script to get it to run my script on the correct APK?
task packageConfig(type: Exec) {
def buildCommit = getBuildCommit()
def buildBranch = getVcsBranchName()
def buildDescription = buildBranch + '-' + buildCommit
def buildDate = getBuildDate()
workingDir buildscript.sourceFile.parent
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
def apk = output.outputFile
def apkDir = apk.getParent();
def inputConfig = '' + project.projectDir + File.separator +
packageConfigXmlFile
def outputConfig = apkDir + File.separator + 'PackageConfig.xml'
commandLine 'python', 'setup-package-config.py', versions.version, inputConfig,
outputConfig, apk, buildDescription
}
}
}
I know that it is possible to specify APK name by using following command in Gradle build file:
setProperty("archivesBaseName", "something")
There are lots of questions here that deal with this. But doing this also results in APK that has flavor and build variant appended to the name. So using above command results in APK that's named something-dev-debug.apk.
Is there a way to completely specify file name without appending any suffixes. I already have appId in my flavors like this:
//...
productFlavors {
dev {
applicationId "com.myapp.dev"
setProperty("archivesBaseName", applicationId)
}
//...
So ideally I would like to produce APK named com.myapp.dev.apk without any extra suffixes or version numbers. Is this possible?
It seems that this shouldn't be done with setPropery. Rather you can define handler that changes output file name as final step by adding following code to the end of android { } section in gradle file:
applicationVariants.all { variant ->
variant.outputs.each { output ->
def file = output.outputFile
def appId = variant.applicationId
def fileName = appId +".apk"
output.outputFile = new File(file.parent, fileName)
}
}
I would like to change the APK output folder and this is what I used to do:
applicationVariants.all { variant ->
variant.outputs.all {
def filePath = "${rootProject.rootDir.absolutePath}/apks/${variant.name}"
println("My Path: " + filePath)
outputFileName = filePath
}
}
However, it didn't work in Gradle 4.1 (Android studio 3.0 preview). Instead of generating the folder as the path above, it generated the above path inside old debug folder like image below:
Does anyone have a solution for this? Thanks.
This is a workaround to keep the output path same after upgrade to gradle 4.x.
applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "../" + outputFileName
}
}
now apk is generated at platforms/android/build/outputs/apk/android-release.apk
From migration guide:
Using the Variant API to manipulate variant outputs is broken with the new plugin. It still works for simple tasks, such as changing the APK name during build time, as shown below:
android.applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "${variant.name}-${variant.versionName}.apk"
}
}
However, more complicated tasks that involve accessing outputFile objects no longer work. That's because variant-specific tasks are no longer created during the configuration stage. This results in the plugin not knowing all of its outputs up front, but it also means faster configuration times.
I had a similar issue, because I needed the output apk in a known folder and not in a folder depending on the computer user name. So I have fixed like this:
applicationVariants.all { variant ->
variant.outputs.all {
def apk = output.outputFile;
def newName = apk.name.replace(".apk", "-v" + variant.versionName + "-RELEASE.apk");
newName = newName.replace("-" + variant.buildType.name, "");
outputFileName = "./" + newName
}
}
With this I get the apk in:
".../outputs/apk/flavorName/buildTypeName/xxx.apk"
Hope it helps you.
After updating Android Studio to version 2.2 I also got an update for the Gradle Plugin (it was 2.1.3):
...
classpath 'com.android.tools.build:gradle:2.2.0'
...
I see the unaligned variant APK file but other variants are not generated anymore. I tryed to enable the zip align:
buildTypes {
release {
minifyEnabled false
zipAlignEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
applicationIdSuffix '.debug'
}
}
but nothing changes. Any ideas?
I "solved" turning back to
classpath 'com.android.tools.build:gradle:2.1.3'
in the project level build.gradle.
EDIT (20160922):
Thanks to Fayder Florez for his response. It's correct, the build environment now produce only one apk (https://code.google.com/p/android/issues/detail?id=212591).
But using by code (that rename de output file name using VERSION CODE and VERSION NAME):
android.applicationVariants.all { variant ->
variant.outputs.each { output ->
def padVersionCode = variant.versionCode.toString();
padVersionCode = padVersionCode.padLeft(5, '0')
def newApkName = "${output.baseName}_${padVersionCode}-${variant.versionName}"
if (!output.zipAlign)
newApkName = newApkName + "_unaligned"
newApkName = newApkName + ".apk"
output.outputFile = new File(output.outputFile.parent, newApkName)
}
}
I get the "_unaligned" appended to the output file name, so I suppose that output.zipAlign is false.
So is the output file really aligned?
EDIT (20161013)
Thanks to ending0421 and it's suggestion to check the apk using the build tool:
zipalign -c -v 4 path/fileName
Now I now that the APK is generated correctly and the zipalign command syays:
Verification succesful
According to this forum: https://code.google.com/p/android/issues/detail?id=212591
"Hi, we don't generate unaligned apks any more. As part of the improvements to speed things, we generate the apk already aligned. So, instead of two, you just get the final one.
#yair.kikielka Thanks."
Reply EDIT (20160922):
So is the output file really aligned?
Yes ! You can verify using
zipalign -c -v 4 path/fileName
When you run this command on apk files which generated by gradle version >=2.2 , you will get "Verification succesful" . That means already aligned .
I am trying to name my application's APK file based on the git branch, which is working by passing a parameter in with jenkins. However, I end up with two APKs, and this is undesirable because one of the Gradle tasks I have uploads the APK to a distributed list. The whole point of renaming this APK is so that people who receive it know exactly what branch they are seeing.
buildTypes {
...
feature {
signingConfig signingConfigs.debug
applicationVariants.all { variant ->
appendVersionNameVersionCode(variant, defaultConfig)
}
}
...
}
...
def appendVersionNameVersionCode(variant, defaultConfig) {
//check if staging variant
if(variant.name == android.buildTypes.feature.name){
def branch = hasProperty('branch') ? branch.replaceAll('origin/', '') : "UNKNOWN"
def file = variant.packageApplication.outputFile
def fileName = "myapp-FEATURE-" + branch + ".apk"
variant.packageApplication.outputFile = new File(file.parent, fileName)
}
}
Then the gradle command is invoked:
gradle assembleFeature --project-dir=/Path/To/myapp/ -Pbranch=origin/development
The two files that are generated from that are:
app-feature.apk
myapp-FEATURE-development.apk
File 1 is not desired, and I would ultimately want to rename that file, instead of generating a new file which is File 2.
Any thoughts?
Turns out, gradle always packages up 2 apks. One that is aligned, and one that isn't.
In order to rename the aligned apk file, I had to add this:
if (variant.zipAlign) {
newName = "myapp-FEATURE-" + branch + "-ALIGNED.apk"
variant.outputFile = new File(file.parentFile, newName);
}