I have an Android project that uses some native libs that are quite big in size.
I have several flavors in this project and some of them don't make use of these libs so I would like to exclude them from the APK.
I know several ways to achieve this but I would like to use some nice code in the build.gradle file to reduce possible errors and learn groovy.
I have to mention that there is a boolean buildConfigField (called DO_IT in this example). If DO_IT is false then JNI libs are to be excluded.
This is the way I do it now:
defaultConfig {
buildConfigField "boolean", "DO_IT", "true"
}
productFlavors {
flavor1 {
// for this flavor JNI libs will be included
}
flavor2 {
// for this flavor JNI libs will NOT be included
buildConfigField "boolean", "DO_IT", "false"
ndk {
abiFilters ''
}
}
}
Remarks:
1 - Consider that I have many flavors with tons of properties and I don't want to replicate the block
ndk {
abiFilters ''
}
but I cannot manage to put this block it inside a method.
2 - The pefect solution would just exclude the libs based on the DO_IT buildConfigField in a routine outside the flavors' blocks EG in the defaultConfig.
android-soexcluder should be the way to go.
Steps are as below:
Modify build.gradle to add the dependencies.
buildscript {
repositories {
//...
}
dependencies {
classpath 'com.jween.gradle:android-soexcluder:1.1'
}
}
apply plugin: 'com.android.application'
apply plugin: 'android-soexcluder'
Exclude the .so from your build flavor
soexcluder {
// Remove all so files according to the flavor1
flavor1 {
exclude "lib/armeabi-v7a/foo.so", "lib/arm64-v8a/bar.so"
}
}
Other option is to add a gradle parameter for packagingOptions: Set doNotStrip packagingOptions to a specific buildType
I use this pattern.
Project build.config
buildscript {
ext.DO_IT = true;
}
Module(app) build.config
productFlavors {
flavor2 {
externalNativeBuild {
cmake {
if (DO_IT) {
targets "someTarget"
}
}
}
}
}
NB.1 ext is mentioned in Extra properties section, an
ExtraPropertiesExtension of Gradle.
NB.2 Per flavor settings like including libraries shoud be controled in project build.config, as Android Studio / Gradle tries to read all setting in all flavors once and causes conflicts or other problems.
NB.3 Please keep in mind that Android Studio does not delete library(.so) files which are already sent to the device. It coufuses test results. Check files in /data/app/(package)/lib/(cpu arch)/ on device and (project)/app/build/intermediates/cmake/(flavor)/obj/(cpu arch)/ in your pc. Clear them manually, especially on the device ones, if you feel the result is wrong.
Clear data from app/storage setting will not clear libraries. Uninstall will clear them, so manual deletion of files is easier I think.
NB.4 Synchronize menu of Device File Explorer of Android Studio does not sync properly for lib or (cpu arch) directories. To see correct results, select /data or /data/app and sync.
Related
I have created a library called mylibrary with two product flavors like virtual and nonvirtual like below
flavorDimensions "dev"
productFlavors {
virtual {
dimension "dev"
}
nonvirtual {
dimension "dev"
}
}
and I have implemented the library in my project LibraryTesting like below
implementation project(path: ':mylibrary')
Now, when I trying to select different build variant its showing errors like "Module 'LibraryTesting.mylibrary' has variant 'nonvirtualDebug' selected, but the module 'LibraryTesting.app' depends on variant 'virtualDebug'"
please find the below image for more information
I want change the library build variant and build the app with that selected variant.
Please help me.
Your app module must decide what flavor to use.
For example, if you want app module to compile myLibrary module and virtual flavor, you need to add the following to android.defaultConfig block in build.gradle of app module:
missingDimensionStrategy 'dev', 'virtual'
Alternatively, if you want to be able to choose the variants from UI without errors, you can create 2 flavors in app module and each flavor will compile the corresponding flavor in myLibrary module:
flavorDimensions "app_dev"
productFlavors {
virtual {
dimension "app_dev"
missingDimensionStrategy 'dev', 'virtual'
}
nonvirtual {
dimension "app_dev"
missingDimensionStrategy 'dev', 'nonvirtual'
}
}
In any case, app module must declare missing dimension strategy
I am trying to add some functionality to an existing application. The app's build.gradle contains several productFlavors and a couple of buildTypes. I have tried to replicate that as best I can in my dynamic-feature module, but I cannot seem to get it to install properly from Android Studio.
I followed the example from: https://github.com/googlearchive/android-dynamic-features to set up my feature module, so my project is structured like
app
features/module/build.gradle
build.gradle
I added a buildType and flavor to the app build.gradle
defaultConfig {
minSdkVersion 24
targetSdkVersion 28
}
dynamicFeatures = [":features:module"]
buildTypes{
myBuildType {
debuggable true
multiDexEnabled true
}
}
flavorDimensions "blah"
productFlavors{
arm64 {
ndk {
abiFilters "arm64-v8a"
}
ext {
abiVersionCode = 5
}
matchingFallbacks = ['defaultFlavor']
}
}
and in the module build.gradle, I have attempted to match that with:
defaultConfig {
minSdkVersion 24
targetSdkVersion 28
}
buildTypes {
dynamic {
multiDexEnabled true
debuggable true
}
}
flavorDimensions "blah"
productFlavors {
arm64 {
ext {
abiVersionCode = 5
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(':app')
}
In my Run->Edit Configuration screen, I have put a checkbox next to both the base app and the module under the dynamic features to deploy section. I am trying to test this on a Nokia 6, with Android 9.0 running on it. The only output I get from the build is:
01/12 22:39:25: Launching 'app' on HMD Global TA-1025.
Installation did not succeed.
The application could not be installed: INSTALL_FAILED_INVALID_APK
The APKs are invalid.
It just happened to me.
Turns out I was setting different flavors for different modules.
Try selecting the same variants under View > Tool Windows > Build Variants.
Please check those things
1) making sure that the AndroidManifest.xml package name was the same as the
build.grade applicationId
2) check package name in your Androidmanifest.xml see whether started with one empty
space character. like " com.example.test" instead of "com.example.test"
and make sure contain at least one dot in your package name
like "com.exampletest" instead of "comexampletest"
https://code.google.com/p/android/issues/detail?id=55841
3)"Build" > "Clean Project"
4)reboot the android system
Try Edit Configurations > Installations Options > Deploy APK from app bundle
I found a solution that resolved my problem. Make sure your libraries and class paths are up to date. I had a class for firebase plugins that were out of date. This problem occurred when using the new graddle. After updating the classpath, everything looks fine.
In my case. I changed this
classpath 'com.google.firebase:firebase-plugins:1.1.0'
to this
classpath 'com.google.firebase:perf-plugin:1.3.1'
I had same problem when I used Flavors (Build variants) in my app.
The solution for me was select another build variant in Build Variants tab (for example, release instead of debug flavor), then select the correct build variant, and then Clean, Rebuild.
I found this solution here: https://stackoverflow.com/a/65630971/6543967
I have an Android app which has a module dependency. This module itself has a jar library as a dependency that comes in two variants, each relative to a build variant of the main app. When I switch build variant in the main app, I managed to automatically select the module's build variant which picks the correct jar, but this is not reflected in the code, where the specific classes from the jar are not found in the build variant specific code.
Here's the related code from the build.gradle files of the main app and the dependency module:
main app - build.gradle
buildTypes {
type1 {initWith(debug)}
type2 {initWith(debug)}
}
productFlavors {
live{}
test{}
}
dependencies {
type1Compile project(path: ':module', configuration: 'one')
type2Compile project(path: ':module', configuration: 'two')
}
module - build.gradle
android {
compileSdkVersion 25
buildToolsVersion "25.0.2"
useLibrary 'org.apache.http.legacy'
publishNonDefault true
defaultConfig {
minSdkVersion 14
targetSdkVersion 22
}
buildTypes {
release {
minifyEnabled = false
proguardFiles getDefaultProguardFile('proguard-android.txt'),
'proguard-rules.pro'
}
one {initWith(release)}
two {initWith(release)}
}
}
dependencies {
oneCompile files('libs/versionOne/mylib.jar')
twoCompile files('libs/versionTwo/mylib.jar')
}
So, when I build testType1 variant, Android Studio automatically selects one variant, and with a clean build it all goes fine. But if I switch to testType2, although module's two variant is selected, the editor will highlight missing classes and methods.
How can I make gradle pick the right jar when I switch between the app's build variants?
Some considerations:
The module needs the library, since it uses a few classes that are common between the 2 versions.
I know this may look like bad project design, but it's an app that has been built by different people in different times, and I now have to develop it "as is".
After some tinkering, I found some clues. First of all, my two jars where the same name, in different folders. After renaming the jars to have also different names, gradle manages to pick the correct one after the build variant switch, provided that an additional "Sync project with gradle files" is done. So now it's not immediate, but eventually it's working.
Getting started with the new android studio it seems very flexible but that usually brings a steep learning curve. Hoping to get some help with that here.
I've built an app and deployed it with this build.gradle file
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "23.0.0"
defaultConfig {
applicationId "me.test.simpleProject
minSdkVersion 10
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
signingConfigs {
release {
storeFile file("../my.keystore.jks")
storePassword System.getenv("and_ks_pw")
keyAlias System.getenv("and_ky_alias")
keyPassword System.getenv("and_k_pw")
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug {
debuggable true
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile 'com.android.support:appcompat-v7:22.2.1'
compile project(":Library:lib_my_lib_a")
compile project(":Library:lib_my_lib_b")
compile project(":Library:lib_my_lib_c")
}
This works pretty well and once I learn the syntax it'll be much easier to use than the previous system.
Now my question.
I would like to basically add a few product flavors.
I currently use google analytics to track clicks, usage stats, simple things like that.
In the past [before android studio] typically distribute my apps in a free/ paid version.
I would like to create say build flavors for:
google play free
google play paid
amazon free
amazon paid
The code for these apps should be mostly identical but they cannot be since, for example adding links to google play in your amazon apps gets you in a bit of trouble over there. So I will need to have unique classes across the build flavors.
I was reading some docs and watching google talks about setting up the build flavors but I am a bit confused.
Can someone help me define the folder structure for this project?
current application structure
test/
.gradle/
.idea/
app/
app/build
app/libs <-- empty
app/src
app/.gitignore
app/app.iml
app/build.gradle
app/proguard-rules.pro
build/
gradle/
Library/lib_a
Library/lib_b
Library/lib_c
.gitignore
build.gradle
gradle.properties
gradlew
gradlew.bat
local.properties
settings.gradle
test.iml
Can I place the new folders anywhere and how do I link them up with grade to properly build the variants?
The first thing you should do, is add these flavors to your gradle script, inside the android section:
productFlavors {
googleFree {
applicationId = "com.your.app.gfree"
}
googlePaid {
applicationId = "com.your.app.gpaid"
}
amazonFree {
applicationId = "com.your.app.afree"
}
amazonPaid {
applicationId = "com.your.app.apaid"
}
}
Note that you can define different package names for your app if needed, as well as some additional properties. Full details: Gradle Plugin - Build Variants
At this point you will have three different concepts in your app now:
Build types: Debug and Release
Product flavors: googleFree, googlePaid, amazonFree and amazonPaid
Build variants: Combination of build types and flavors
googleFreeDebug
googleFreeRelease
googlePaidDebug
googlePaidRelease
amazonFreeDebug
amazonFreeRelease
amazonPaidDebug
amazonPaidRelease
Flavor Specific resources:
For defining resources, you should have:
app
src
main
java
res
googlePaid
java
res
googleFree
java
res
amazonPaid
java
res
amazonFree
java
res
With that structure in place, you can place all your shared stuff under main, while the flavor specific stuff would live on each flavor's folder. Then, when building a specific variant of your app, android and gradle will resolve the right resources for you. If you are interested in more details, see Resource Merging.
Once the flavors have been added to your build.gradle file, if you sync your project, Android Studio will be able to pick your build variants and you may select these from the IDE:
To use flavors in your app you should have a structure like this:
app
src
flavor1
java
res
flavor2
java
res
main
java
res
You have to define the flavors in the build.gradle:
productFlavors {
flavor1 {
//
}
flavor2 {
//
}
}
Then you can have different dependencies using:
dependencies {
releaseCompile
debugCompile
flavor1Compile
flavor1DegubCompile
}
For example:
dependencies {
freeCompile 'com.google.android.gms:play-services:7.5.0'
}
How is it possible to identify the current flavor being compiled. I'm trying to add a file to compile only if I'm compiling a certain product flavor.
buildTypes {
android.applicationVariants.all { variant ->
variant.productFlavors.each() { flavor ->
if (flavor.name.equals(currentFlavorName)) {
The problem is that I can't seem to find where the currentFlavourName of the flavor which I am currently building is located.
just put the strings you want for flavor1 into:
src/flavor1/res/values/strings.xml
and the strings for flavor2 into:
src/flavor2/res/values/strings.xml
no need to put logic into your gradle file
Android uses a unique build process regarding your resources for different flavors and it is very easy to control.
if you set up your main source:
project-name
------------/app
---------------/src
-------------------/main
------------------------/res
----------------------------/values
------------------------/java
-------------------/development
-------------------------------/res
-----------------------------------/values
-------------------------------/java
-------------------/production
------------------------------/res
----------------------------------/values
------------------------------/java
This would be a bottom up approach from product flavor into main. Meaning if you have a strings.xml with items having the same name existing in development/res/values and have values that also exist in main/res/values/strings.xml these will be over written (and same would go for the production flavor) based on the build variant defined in your gradle file.
android {
productFlavors {
production {
applicationId "com.test.prod"
versionName "1.0.0"
}
development {
applicationId "com.test.dev"
versionName "1.0.0"
}
}
I don't know if exits a method to get the currentFlavor. I haven't found it yet.
A ugly solution can be
variant.productFlavors.each() { flavor ->
if (flavor.name.equals("flavor1")) {
//..........
}
}
However, if you want to be able to control which strings.xml you are using, you can achieve it in different ways.
First of all you can just define a xml file in your flavor folder.
app/src/main/res/values/ -> for common resources
app/src/flavor1/res/values -> resources for flavor1
app/src/flavor2/res/values -> resources for flavor2
This doesn't require any config in your build.gradle script.
A second option is to define a resource value using build.gradle.
Something like:
productFlavors {
flavor1 {
resValue "string", "app_name", "IRCEnterprise"
}
//....
}
Another option is to create some field in your BuildConfig class using this kind of script.
productFlavors {
flavor1 {
buildConfigField "String", "name", "\"MY FLAVOR NAME\""
}
}