I'm dealing with a project that has a large number of dependencies. This has become a problem because when adding new SDK's we're going past the maximum limit of methods that an APK can hold (65536).
I'm looking at using the new build system in Android Studio in order to create build variants that rely on subsets of our dependencies. I was able to find a number of tutorials that help with creating different class alternatives using build flavours, but I didn't find anything regarding specifying different dependencies for each flavour. Is this even possible? What would the whole workflow look like?
For example, I would like to create a build variant that removes Flurry support. I would be able to create a different class that does not instantiate the Flurry SDK, but I would also like to be able to remove the lib dependency entirely, in order to reduce the total number of methods in the APK.
I'm also open to any suggestion regarding managing this kind of issue :)
This works (making sure dependencies are listed after productFlavors):
android {
...
productFlavors {
flavor1 { ...
...
dependencies {
flavor1Compile ...
}
But this won't work:
android {
...
dependencies {
flavor1Compile ...
}
...
productFlavors {
flavor1 { ...
...
Related
I'm trying to build a flavor of my app that includes a very heavy dependency and is only going to be used in certain builds for testing and offline development (dependency is Wiremock for Android). However I can't seem to find any flavor variant dependency declarations that also use api() and exclude.
Before I decided to move the dependency to a build variant, I could declare the dependencies like so:
dependencies {
//WireMock - Do not put in release builds bc of large size
api("com.github.tomakehurst:wiremock:2.18.0") {
exclude("org.apache.httpcomponents", "httpclient")
exclude("org.ow2.asm", "asm")
exclude("org.json", "json")
}
api("org.apache.httpcomponents:httpclient-android:4.3.5.1")
}
I would love to restrict this dependency to my build flavor, which I have simply called "mock", something like:
dependencies: {
"mockImplementation"(
api("com.github.tomakehurst:wiremock:2.18.0") {
exclude("org.apache.httpcomponents", "httpclient")
exclude("org.ow2.asm", "asm")
exclude("org.json", "json")
}
api("org.apache.httpcomponents:httpclient-android:4.3.5.1")
})
}
This is obviously very wrong but I am unsure of how to go about formatting with the api and exclude dependency notations as I cannot find very many examples when it comes to also combining these with a build flavor.
After a lot of playing around I ended up with:
// WireMock - Do not put in release builds bc of large size, restrict to mock flavors
"mockImplementation"(mockApi("com.github.tomakehurst:wiremock:2.18.0") {
// Using Android Version Instead
exclude("org.apache.httpcomponents", "httpclient")
//Was getting a classpath conflict for org.objectweb.asm.AnnotationVisitor which is a part of 'net.minidev:asm'
exclude("org.ow2.asm", "asm")
//Was getting this warning, so decided to ignore this version included by WireMock.
//Warning:Dependency org.json:json:20090211 is ignored as it may be conflicting with the internal version provided by Android.
//In case of problem, please repackage with jar to change the class packages
exclude("org.json", "json")
})
"mockImplementation"(mockApi("org.apache.httpcomponents:httpclient-android:4.3.5.1") {})
Note that the "mockApi" was necessary rather than just using "api" to actually constrain the variant.
I have two flavors in my app and I want a different applicationId for each one. This is what I have:
defaultConfig {
applicationId "com.mycompany.app"
//more stuff
}
...
productFlavors {
firstflavor {
applicationIdSuffix '.first'
//more stuff
}
secondflavor {
applicationIdSuffix '.second'
//more stuff
}
}
In AndroidManifest.xml, I have package="com.mycompany.app", and all the code is under that package.
When I assemble it with, say, flavor firstflavor, I get this error:
Error:The generated com.mycompany.app.first.R class cannot be
found
Shouldn't R.class be generated under com.company.app instead of com.company.app.first?
How is it package related to application id?
If I remove the application id flavor configuration (applicationIdSuffix), everything works properly.
EDIT
I have read this here:
If the BuildConfig.java and R.java files was set to the applicationId
rather than the package name, then the code which referenced these
would need to have different imports for different build variants – in
other words you would need to have separate source sets (with huge
amounts of duplication) for your different build flavors in order to
access these. This would have some quite profound implications for the
maintainability of our code.
However, error says that com.mycompany.app.first.R cannot be found. But, com.mycompany.app.first is the application id, not the package name! Why is that?
I'm using Android Annotations 3.3.2
I found the solution here. As I said, I'm using Android Annotations, so I needed to configure package name in apt plugin:
apt {
arguments {
//more things
resourcePackageName "com.mycompany.app" //add this line
}
}
and everything is working fine.
Use the file menu and do an invalidate caches and restart.
Use the invalidate and restart option there.
It'll rebuild the indexes and solve your issue. If that doesn't work, also do a clean build from the build menu and there'll been a gradle sync button on your toolbar, use that. Mostly the invalidate and restart should solve your issue of you don't have any other errors
I'm working on an Android application that has two different flavours and I'm looking at the Gradle Play Publisher to automate the release to the alpha channels by using the Gradle wrapper command line.
Each of the two flavours corresponds to a different developer account on Google Play.
The issue that I'm facing now, is that the Play Publisher plugin only takes one Google Play configuration ( service account + auth key ).
I'm not particularly familiarised with Gradle, but I'm guessing is flexible enough to iterate over the flavours and execute a task with a 'per-flavour' buildscript config.
Does anyone know how can I accomplish this? Maybe examples of other projects that manipulate buildscript configuration based on flavours?
Thanks in advance.
Relevant part of the build.gradle script.
android {
...
productFlavors {
flavour1 {
applicationId 'com.example.1'
}
flavour2 {
applicationId 'com.example.2'
}
}
}
...
play {
serviceAccountEmail = '<SERVICE_ACCOUNT>'
pk12File = file('<PATH_TO_KEY>')
}
Adding two solutions that I found in Github to work around this issue, as no official support for this feature is provided by the project developers.
Solution #1: Using environment variables
https://github.com/Triple-T/gradle-play-publisher/issues/65
Solution #2: Using Gradle Task and custom config files
https://github.com/Triple-T/gradle-play-publisher/issues/76#issuecomment-121756953
You can follow the complete discussion here:
https://github.com/Triple-T/gradle-play-publisher/issues/76
So ultimately I'm trying to separate my integration tests from the unit tests in an Android Studio project. I've found a few resources on the subject:
http://selimober.com/blog/2014/01/24/separate-unit-and-integration-tests-using-gradle/
https://blog.safaribooksonline.com/2013/08/22/gradle-test-organization/
Separating integration tests from unit tests in Android Studio
All these seem to indicate that the way to go is to create a new sourceSet for the integration tests, and then to create a new test task which builds and runs the tests in that source set. I can't get past the first step of creating a source set which is recognized by Android Studio.
Here's what I have within app/build.gradle, which builds without errors, but does not result in an integrationTest source root I can add classes to:
android{
...
sourceSets{
integrationTest {
java.srcDir('src/integrationTest/java')
}
}
}
My questions are:
Where precisely do I have to add the sourceSets block? In build.gradle? in app/build.gradle? In app/build.gradle inside the android block?
Once I've added my source set in he right place using the correct syntax, is this sufficient for Android Studio to detect and present it in the UI along side the main and test sources, or are there additional steps?
edit:
I've attempted to follow the instructions in marius' answer, but integrationTest isn't showing up in my build variants. Here's what I'm seeing:
This is enough:
android{
...
productFlavors{
integrationTest {
}
}
}
Regarding your 1st question: The productFlavors block should be in your app/build.gradle, inside android block.
Regarding your 2nd question: Once you add this to your build.gradle file, you also need to create your folders /src/integrationTest and /src/integrationTest/java . Once that is done, sync your gradle files and choose your new Build Variant from the Build Variant window, in order for the IDE to detect it as the active source folder.
I have an Android project (already ported to Android Studio and using Gradle) that is made up of different modules.
The project is actually used to create two different apps, where the code is pretty much the same, except for some resources.
Thus the resources have been split into two different modules.
The original author of this project used to work in Eclipse and switch the resource modules included in the dependencies based on which app he wanted to build. And he also used to change by hand the package name in AndroidManifest.xml
I would like to automate all of this and still have a single code base, but have two build targets with specific modules for each target. Is that doable with Gradle?
Update:
To make things even harder, my project has a hierarchy that is pretty much the following:
--+--MainProject
+--LibData
+--LibBase
+--LibResA
+--LibResB
Where:
MainProject depends on LibBase and LibData.
LibData depends on LibBase
LibBase either depends on LibResA or LibResB based on the final APK that I need to build.
As suggested, I've tried implementing this with flavors by adding in the MainProject build.gradle the following:
productFlavors {
producta {
}
productb {
}
}
And then in LibBase I've added the following to its build.gradle:
dependencies {
productaCompile project(':LibResA')
productbCompile project(':LibResB')
}
But then, when I build the project, LibData can't find the classes and resources inherited from LibBase. So now I'm stuck with this error. To me it looks like LibBase isn't being copied to the intermediates of LibData. That way LibData can't resolve the classes in LibBase, but it's just my assumption.
Update 2:
I kept investigating this issue and now I've changed my build.gradle files to look like this:
Main Project build.gradle:
defaultPublishConfig "productaRelease"
publishNonDefault true
productFlavors {
producta {
applicationId "com.producta"
}
productb {
applicationId "com.productb"
}
}
dependencies {
compile project(':LibData')
}
LibData build.gradle (has no product flavors, just the dependencies):
dependencies {
compile project(':LibBase')
}
LibBase build.gradle:
defaultPublishConfig "productaRelease"
publishNonDefault true
productFlavors {
producta {
}
productb {
}
}
dependencies {
productaCompile project(path: ':LibResA')
productbCompile project(path: ':LibResB')
}
This way I get no errors when doing the usual gradle clean build but I can see that the resources included are always those of LibResA just like the defaultPublishConfig is the only one used at all times.
If I open this project in Android Studio (0.8.1 atm) the result is that if I try to switch the build variant of the LibBase module and set it to productbRelease, the following error is being shown: Error:Module 'LibBase' has variant 'productbRelease' selected, but the module ''LibData'' depends on variant 'productaRelease'.
I'm running out of ideas.
Since you already have the product flavors:
productFlavors {
producta {
}
productb {
}
}
Define your dependencies prefixed with flavor name.
Example:
dependencies {
productaImplementation 'com.google.android.gms:play-services:11.0.2'
productbImplementation 'com.google.android.gms:play-services:12.0.1'
}
Common dependencies will be defined normally.
Now build apk for individual flavors.
Not the best way to do it, but if productFlavors is not enough to specify conditional dependencies you can rely on an inline if and evaluate it based on some value that can be injected via external properties.
For example here is how I toggle LeakCanary (no-op is just the empty implementation of the other one):
build.gradle
dependencies {
compile "com.squareup.leakcanary:leakcanary-android"+(project.ext.has("leakCanary")?"":"-no-op")+":1.3.1"
}
To build with com.squareup.leakcanary:leakcanary-android:1.3.1:
$ ./gradlew :app:assembleDebug -PleakCanary
By default it builds with the empty implementation com.squareup.leakcanary:leakcanary-android-no-op:1.3.1:
$ ./gradlew :app:assembleDebug
This provides a quick and more flexible way to toggle things using build command, but too much of it and things will get messy real quick.
Yes, it is. New Android build system based on Gradle supports your use case with its concept of product flavors. http://tools.android.com/tech-docs/new-build-system/user-guide
Note that you will likely want to switch from Eclipse to Android Studio when you do migration to Gradle build.