ABI Split failed in NativeScript 2.3.0 - android

I’ve asked for help in the general group but most people didnt get it work as well.
Im having a huge problem ridiculous apk size of my simple app. I used #markosko nativescript filter to reduce the app to 14MB from 17.2MB, even nativescript-snapshot couldn't help still 17MB for release version.
I tried using the ABI split sample in the nativescipt documentation but what I noticed is that it’s trying to split but the glade is using same name for all the apks so I came up with this in my app.glade
def tnsBuildMultipleApks=true;
android {
defaultConfig {
generatedDensities = []
applicationId = "com.maliyo.oneclick"
versionCode Integer.parseInt("" + "1" + "0")
}
aaptOptions {
additionalParameters "--no-version-vectors"
}
if (Boolean.valueOf(tnsBuildMultipleApks)) {
splits {
abi {
enable true
reset()
include 'armeabi', 'armeabi-v7a', 'x86', 'mips'
universalApk true
}
}
}
}
def getDate() {
def date = new Date()
def formattedDate = date.format('yyyyMMdd')
return formattedDate
}
// map for the version code that gives each ABI a value
ext.versionCodes = [
'F0F1F2X86Debug':1, 'F0F1F2ArmeabiDebug':2, 'F0F1F2Armeabi-v7aDebug':3, 'F0F1F2MipsDebug':4,
'F0F1F2X86Release':1, 'F0F1F2ArmeabiRelease':2, 'F0F1F2Armeabi-v7aRelease':3, 'F0F1F2MipsRelease':4
]
// For each APK output variant, override versionCode with a combination of
// ABI APK value * 100 + defaultConfig.versionCode
android.applicationVariants.all { variant ->
// assign different version code for each output
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
println("******************************************************")
println(output);
println(output.getVariantOutputData().getFullName())
if (Boolean.valueOf(tnsBuildMultipleApks)) {
def file = output.outputFile
// version at the end of each built apk
//output.outputFile = new File(file.parent, file.name.replace(".apk", "-" + android.defaultConfig.versionName + "-" + getDate() + ".apk"))
output.outputFile = new File(file.parent, file.name.replace(".apk", "-" + output.getVariantOutputData().getFullName() + "-" + getDate() + ".apk"))
output.versionCodeOverride =
//(assd++) * 100
project.ext.versionCodes.get(output.getVariantOutputData().getFullName(), 0) * 100
+ android.defaultConfig.versionCode
}
}
}
}
/**/
Fine, it splits but I think because I hacked the filename at output the adb couldn't find one to push the apk to the device or emulator due to the naming pattern, maybe, just saying apk not found.
I tried to manually send the appropriate apk to the device via USB, it app installed successfully but it crashes after splashscreen saying metadata/treeNodeStream.dat could not be loaded
UPDATE
#plamen-petkov thanks so much for your contribution, I agree with you that it work fine, when you build one after another changing the abi filter. But with this in my app.gradle, I managed to build multiple apks successfully and tested and OK.
but is like the the tns is only pushing appname-debug.apk or appname-release.apk to the adb. I can toggle this splitting off with tnsBuildMultipleApks and maybe when Im still testing I can turn it off and use tns run android and when I want to make final build and it turn it one again as it works fine with tns build android --release ....
// Add your native dependencies here:
// Uncomment to add recyclerview-v7 dependency
//dependencies {
// compile 'com.android.support:recyclerview-v7:+'
//}
import groovy.json.JsonSlurper //used to parse package.json
def tnsBuildMultipleApks=true;
String content = new File("$projectDir/../../app/package.json").getText("UTF-8")
def jsonSlurper = new JsonSlurper()
def appPackageJson = jsonSlurper.parseText(content)
android {
defaultConfig {
generatedDensities = []
applicationId = appPackageJson.nativescript.id
versionCode = appPackageJson.version_code ?: 1
}
aaptOptions {
additionalParameters "--no-version-vectors"
}
if (Boolean.valueOf(tnsBuildMultipleApks)) {
splits {
abi {
enable true
reset()
include 'x86', 'armeabi-v7a', 'arm64-v8a'
universalApk true
}
}
}
}
// map for the version code that gives each ABI a value
ext.versionCodes = [
'x86':1, 'armeabi-v7a':2, 'arm64-v8a':3
]
// For each APK output variant, override versionCode with a combination of
// ABI APK value * 100 + android.defaultConfig.versionCode
// getAbiFilter() not working for me so I extracted it from getFullname()
if (Boolean.valueOf(tnsBuildMultipleApks)) {
android.applicationVariants.all { variant ->
println(appPackageJson)
println(android.defaultConfig.versionCode)
println(android.defaultConfig.applicationId)
def name
def flavorNamesConcat = ""
variant.productFlavors.each() { flavor ->
flavorNamesConcat += flavor.name
}
flavorNamesConcat = flavorNamesConcat.toLowerCase()
println(flavorNamesConcat)
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
//You may look for this path in your console to see what the values are
println("******************************************************")
println(output); println(output.getVariantOutputData().getFullName())
def abiName = output.getVariantOutputData().getFullName().toLowerCase().replace(flavorNamesConcat, "").replace(project.ext.selectedBuildType, "")
println(abiName)
def file = output.outputFile
output.versionCodeOverride =
project.ext.versionCodes.get(abiName, 0) * 100
+ android.defaultConfig.versionCode
def apkDirectory = output.packageApplication.outputFile.parentFile
def apkNamePrefix = output.outputFile.name.replace(".apk", "-" + abiName)
if (output.zipAlign) {
name = apkNamePrefix + ".apk"
output.outputFile = new File(apkDirectory, name);
}
name = apkNamePrefix + "-unaligned.apk"
output.packageApplication.outputFile = new File(apkDirectory, name);
}
}
}
}

ABI splits can be useful if you use them one at a time. Here's an example:
android {
...
splits {
abi {
enable true
reset()
include 'armeabi-v7a'
}
}
...
}
The resulting .apk file will only contain the libraries necessary for armeabi-v7a devices, because it's the only architecture mentioned in the ABI splits configuration above. The ABIs available are 'arm64-v8a', 'armeabi-v7a', 'x86' as pointed out in the documentation, so you can't use 'armeabi' or 'mips' architectures.
Furthermore you don't need this line: 'universalApk true', because what it does is ignoring the splits and making one .apk file containing all the provided architectures and you want the opposite.
You can also follow the progress of this issue as it will decrease the .apk size even more.
Hope this helps!

This work so well for me now, both generating apks and tns run android runs fine now, thanks.
// Add your native dependencies here:
// Uncomment to add recyclerview-v7 dependency
//dependencies {
// compile 'com.android.support:recyclerview-v7:+'
//}
import groovy.json.JsonSlurper //used to parse package.json
def tnsBuildMultipleApks=true;
String content = new File("$projectDir/../../app/package.json").getText("UTF-8")
def jsonSlurper = new JsonSlurper()
def appPackageJson = jsonSlurper.parseText(content)
android {
defaultConfig {
generatedDensities = []
applicationId = appPackageJson.nativescript.id
versionCode = appPackageJson.version_code ?: 1
}
aaptOptions {
additionalParameters "--no-version-vectors"
}
if (Boolean.valueOf(tnsBuildMultipleApks)) {
splits {
abi {
enable true
reset()
include 'x86', 'armeabi-v7a', 'arm64-v8a'
universalApk true
}
}
}
}
// map for the version code that gives each ABI a value
ext.versionCodes = [
'x86':1, 'armeabi-v7a':2, 'arm64-v8a':3
]
// For each APK output variant, override versionCode with a combination of
// ABI APK value * 100 + android.defaultConfig.versionCode
// getAbiFilter() not working for me so I extracted it from getFullname()
if (Boolean.valueOf(tnsBuildMultipleApks)) {
android.applicationVariants.all { variant ->
println(appPackageJson)
println(android.defaultConfig.versionCode)
println(android.defaultConfig.applicationId)
def name
def flavorNamesConcat = ""
variant.productFlavors.each() { flavor ->
flavorNamesConcat += flavor.name
}
flavorNamesConcat = flavorNamesConcat.toLowerCase()
println(flavorNamesConcat)
variant.outputs.each { output ->
if (output.outputFile != null && output.outputFile.name.endsWith('.apk')) {
//You may look for this path in your console to see what the values are
println("******************************************************")
println(output); println(output.getVariantOutputData().getFullName())
def abiName = output.getVariantOutputData().getFullName().toLowerCase().replace(flavorNamesConcat, "").replace(project.ext.selectedBuildType, "")
println(abiName)
def file = output.outputFile
def versionCode = project.ext.versionCodes.get(abiName, 0);
output.versionCodeOverride =
project.ext.versionCodes.get(abiName, 0) * 100
+ android.defaultConfig.versionCode
def apkDirectory = output.packageApplication.outputFile.parentFile
println(output.outputFile.name)
def apkNamePrefix = ""
if(versionCode){
apkNamePrefix = output.outputFile.name.replace(".apk", "-" + abiName)
}
else {
apkNamePrefix = output.outputFile.name.replace(".apk", "")
}
if (output.zipAlign) {
name = apkNamePrefix + ".apk"
output.outputFile = new File(apkDirectory, name);
}
name = apkNamePrefix + "-unaligned.apk"
output.packageApplication.outputFile = new File(apkDirectory, name);
}
}
}
}

Related

flutter set version name in apk filename

I tried many options in android/app/build.gradle
but none seems to be working.
for example I tried with below code in default config block
archivesBaseName = "AppName-${versionName}-${new Date().format('yyMMdd')}"
all example seems to be not related to flutter.
You can try this in your gradle file
buildTypes {
release {
signingConfig signingConfigs.debug
applicationVariants.all { variant ->
variant.outputs.all {
def appName = "your_app_name_"
def buildType = variant.variantData.variantConfiguration.buildType.name
def newName
if (buildType == 'debug'){
newName = "app-${variant.getFlavorName()}-debug.apk"
} else {
newName = "${appName}${defaultConfig.versionName}_${variant.getFlavorName()}.apk"
}
outputFileName = newName
}
}
}
}
This code works with regular builds and flavors.
Add it in the build.gradle (the source is https://stackoverflow.com/a/58392819/7198006)
android.applicationVariants.all { variant ->
variant.outputs.all { output ->
def builtType = variant.buildType.name
def versionName = variant.versionName
def versionCode = variant.versionCode
def flavor = variant.flavorName
outputFileName = "app-${flavor}-${builtType}-${versionName}-${versionCode}.apk"
}
}
You can open pubspec.yaml and search on version and edit it
Version: 1.0.0+1

Android: How to change specific name of the generated apk file in Android Studio?

By default IDE genarate a apk like app-debug.apk or app-release.apk file but I need to generate specific name of the Apk of the App.
For Example:
My application name is iPlanter so i need to generate iPlanter-debug.apk or iPlanter-release.apk instead of app-debug.apk or app-release.apk respectively.
Thanks,
Just add
archivesBaseName = "NAME_YOU_WANT"
in the android{} part of your gradle file.
You'll get "NAME_YOU_WANT-release.apk" as name of the generated file.
Step 1:
Go to root of the main project, under app , right click on app and refactor the app into specific name (example iPlanter) and press ok
Step 2:
Go to Project Setting file which is setting.gradle file
setting.gradle file contains
include ':app'
Now need to replace app by specific name.
For Example
app replace by iPlanter in include ':app'
it looks like below
include ':iPlanter'
then Sync project, after that run your application.
Finally, App generate an apk like iPlanter-debug.apk or iPlanter-release.apk file.
You just have to add following one line of code in app level gradle.
For name only
archivesBaseName = "NAME_YOU_WANT"
defaultConfig {
applicationId "com.PACKAGENAME"
minSdkVersion Integer.parseInt(MIN_SDK_LIBRARY)
targetSdkVersion Integer.parseInt(TARGET_SDK)
versionCode 11
versionName "2.3"
multiDexEnabled true
archivesBaseName = "NAME_YOU_WANT"
}
Name with version
archivesBaseName = "NAME_YOU_WANT" + versionName
defaultConfig {
applicationId "com.PACKAGENAME"
minSdkVersion Integer.parseInt(MIN_SDK_LIBRARY)
targetSdkVersion Integer.parseInt(TARGET_SDK)
versionCode 11
versionName "2.3"
multiDexEnabled true
archivesBaseName = "NAME_YOU_WANT" + versionName
}
You can use this for app name with current date and version
android {
def version = "2.4";
def milestone = "1";
def build = "0";
def name = getDate()+"APP NAME WHAT YOU WANT"+"v"+version
signingConfigs {
config {
….
}
}
compileSdkVersion Integer.parseInt(COMPILE_SDK)
buildToolsVersion BUILD_TOOLS_VERSION
defaultConfig {
applicationId "com.PACKAGENAME"
minSdkVersion Integer.parseInt(MIN_SDK_LIBRARY)
targetSdkVersion Integer.parseInt(TARGET_SDK)
versionCode 11
versionName "2.3"
multiDexEnabled true
}
buildTypes {
debug {
applicationVariants.all { variant ->
variant.outputs.each { output ->
def apk = output.outputFile;
def newName;
newName = apk.name.replace("-" + variant.buildType.name, "")
.replace(project.name, name);
newName = newName.replace("-", "-" + version + "-" + milestone +
"-" + build + "-");
output.outputFile = new File(apk.parentFile, newName);
}
}
}
This will help you. This code will create app name like iPlanter-release.apk or iPlanter-debug.apk
buildTypes {
applicationVariants.all { variant ->
variant.outputs.each { output ->
project.ext { appName = 'iPlanter' }
def newName = output.outputFile.name
newName = newName.replace("app-", "$project.ext.appName-")
output.outputFile = new File(output.outputFile.parent, newName)
}
}
}
Update:
applicationVariants.all { variant ->
variant.outputs.all {
outputFileName = "iPlanter_${variant.versionName}(${variant.versionCode}).apk"
}
}
It set name like
iPlanter_0.0.1(25).apk
For Android Studio 3, this works for me:
applicationVariants.all { variant ->
variant.outputs.all { output ->
outputFileName = new File("AppName-" + variant.versionName + ".apk");
}
}
Try this code:
defaultConfig{
applicationVariants.all { variant ->
changeAPKName(variant, defaultConfig)
}
}
def changeAPKName(variant, defaultConfig) {
variant.outputs.each { output ->
if (output.zipAlign) {
def file = output.outputFile
output.packageApplication.outputFile = new File(file.parent, "Your APK NAME")
}
def file = output.packageApplication.outputFile
output.packageApplication.outputFile = new File(file.parent, "Your APK NAME")
}
}
For Android Studio 3.1 this works for me:
android {
...............
...............
applicationVariants.all { variant ->
changeAPKName(variant, defaultConfig)
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
.................................
.................................
}
and
def changeAPKName(variant, defaultConfig) {
variant.outputs.all { output ->
outputFileName = new File("xxxxx" + variant.versionName +".apk")
}
}
On my PC, it suffices to rename (through refactor) app to the desired name yourName. After that, include ':app' in setting.grandle file is changed to include ':yourName'. However, in my case I need to close/reopen Android Studio because of sync error. As a result, obtain apk something like yourName-debug.apk and yourName-release.apk.

How to get ABI name in Gradle outputs iterator

I'm composing .apk filename using current app version and flavor name. I'd like to add current ABI split name as well, but only if it's a universal apk.
My relevant build.gradle sections:
buildTypes {
release {
applicationVariants.all { variant ->
variant.outputs.each { output ->
def flavor = .... // some code to parse flavor & determine an appropriate string from it
output.outputFile = new File(output.outputFile.parent, "app_" + flavor + "_0" + variant.versionCode + ".apk")
}
}
}
}
productFlavors {
deploy {
splits {
abi {
enable true
reset()
include 'armeabi-v7a' //select ABIs to build APKs for
universalApk true //generate an additional APK that contains all the ABIs
}
}
}
}
Currently this config generates two .apks, but they both have the same file name as I don't know how to get the ABI name, so the one generated later overwrites the one generated before.
So, what is the equivalent variant.productFlavors.get(0) for current ABI split?
That's very strange as flavor and ABI-name is automatically added to build name (if you make corresponding assemble)
can you try completely remove your custom made naming
applicationVariants.all { variant ->
variant.outputs.each { output ->
def flavor = .... // some code to parse flavor & determine an appropriate string from it
output.outputFile = new File(output.outputFile.parent, "app_" + flavor + "_0" + variant.versionCode + ".apk")
}
}
and instead of that try to add to defaultConfig this line
archivesBaseName = "app_${versionCode}"
If this is will not resolve you issues you can try to get abi from output
output.getFilter(com.android.build.OutputFile.ABI)
The equivalent is output.getFilter(com.android.build.OutputFile.ABI).
Note that, starting with Android Studio 3.0, you should use outputFileName and variant.outputs.all instead:
applicationVariants.all { variant ->
variant.outputs.all { output ->
outputFileName = "app_" + output.getFilter(com.android.build.OutputFile.ABI) + "_0" + variant.versionCode + ".apk"
}
}

Android - Only execute Gradle task on release build variant

I am trying to configure my build.gradle file to only execute a gradle task when the release build variant is selected. So far, my task always gets executed, whether it is in my debug or release build types or signing configs. I have tried adding my task inside an applicationsVariants block and check if it is the release variant, but it just loops through all variants.
applicationVariants.all { variant ->
variant.outputs.each { output ->
...
}
}
I know that both the debug and release tasks always run for whichever build variant you choose. Is it possible to execute some code only when creating a build for release? If so, where does that code go? Thanks!
I have read through every Stackoverflow question on this, but none of the answers really did I am wanting. My end goal is when I select the "release" build variant for a Play Store build, a message is posted to our server. I do not want this to happen when just debugging.
Add doFirst or doLast for the build type you are interested in.
android.applicationVariants.all { variant ->
if ( variant.buildType.name == "release"){
variant.assemble.doLast { // Can also use doFirst here to run at the start.
logger.lifecycle("we have successfully built $v.name and can post a messaage to remote server")
}
}
}
I had to do something like this to check build version:
buildTypes {
applicationVariants.all { variant ->
variant.outputs.each {output ->
def project = "AppName"
def separator = "_"
/*def flavor = variant.productFlavors[0].name*/
def buildType = variant.variantData.variantConfiguration.buildType.name
def versionName = variant.versionName
def versionCode = variant.versionCode
def date = new Date();
def formattedDate = date.format('yyyyMMdd_HHmm')
if (variant.buildType.name == "release"){
def newApkName = project + separator + "v" + versionName + separator + versionCode + separator + buildType + separator + formattedDate + ".apk"
output.outputFile = new File(output.outputFile.parent, newApkName)
}
if (variant.buildType.name == "debug"){
def newApkName = project + separator + "v" + versionName + separator + versionCode + separator + buildType + ".apk"
output.outputFile = new File(output.outputFile.parent, newApkName)
}
}
} }

Build release apk with customize name format in Android Studio

I want the .apk to be built with the following name format (with timestamp).
How can I set it?
format : {app_name}{yyyymmddhis}.apk
Now, it is fixed with the name {app_name}-{release}.apk
in the build.gradle file, you should change/add buildTypes like this:
buildTypes {
release {
signingConfig signingConfigs.release
applicationVariants.all { variant ->
def file = variant.outputFile
def date = new Date();
def formattedDate = date.format('yyyyMMddHHmmss')
variant.outputFile = new File(
file.parent,
file.name.replace("-release", "-" + formattedDate)
)
}
}
}
====== EDIT with Android Studio 1.0 ======
If you are using Android Studio 1.0, you will get an error like this:
Error:(78, 0) Could not find property 'outputFile' on com.android.build.gradle.internal.api.ApplicationVariantImpl_Decorated#67e7625f.
You should change the build.Types part to this:
buildTypes {
release {
signingConfig signingConfigs.releaseConfig
applicationVariants.all { variant ->
variant.outputs.each { output ->
def date = new Date();
def formattedDate = date.format('yyyyMMddHHmmss')
output.outputFile = new File(output.outputFile.parent,
output.outputFile.name.replace("-release", "-" + formattedDate)
)
}
}
}
}
As a side note, I would really like to know where people get the objects structures of gradle build objects.
To construct this I've used some trial and errors, a bit of Googling, and this (which is out of date)
android {
...
buildTypes {
debug {
signingConfig signingConfigs.debug
}
release {
signingConfig signingConfigs.release
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
applicationVariants.all { variant ->
variant.outputs.each { output ->
def flavor = "default";
if (variant.productFlavors.size() > 0)
flavor = variant.productFlavors.get(0);
def initials = "DefaultFlavor";
if (flavor.name == "flavor1")
initials = "F1";
else if (flavor.name == "flavor2")
initials = "F2";
def build = "Debug";
if (variant.buildType.name == "release")
build = "Release"
def finalName = variant.versionCode + "-" + initials + "-" + build + "-v" + flavor.versionName + "-MyAppName.apk";
output.outputFile = new File(output.outputFile.parent, finalName)
}
}
}
...
}
Ok, So as a side note, if you would like to determine the names of your libs project here is a quick script, make sure you put it in each of your library gradle build files.
android {
...
defaultConfig {
minSdkVersion 10
targetSdkVersion 16
versionCode 1
versionName "1.0.4"
project.version = versionName
}
libraryVariants.all { variant ->
variant.outputs.each { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.aar')) {
def fileName = "${archivesBaseName}-v${version}.aar"
output.outputFile = new File(outputFile.parent, fileName)
}
}
}
...
}
You should change something like this. Doing dynamically.
project.archivesBaseName = {app_name}{yyyymmddhis};
But I have read that this is going to be deprecated.
Creating properties on demand (a.k.a. dynamic properties) has been deprecated and is scheduled to be removed in Gradle 2.0. Deprecated dynamic property: "archivesBaseName" on "root project 'myapp'", value: "AnotherName".

Categories

Resources