Custom Android Library Configurations - android

I have a library that I've currently broken into two different gradle modules. One of the gradle modules provides a stub/dummy implementation of my library and I have another gradle module that provides the real implementation. The reason for this, is that in certain flavors of my app, I want to only use stub implementation where the library does nothing whereas other flavors, I want to provide the real implementation.
In my app's build.gradle, I use the following "dependencies" block.
dependencies {
flavorOneCompile project(":realLibrary")
flavorTwoCompile project(":stubLibrary")
}
This solution seems to work okay but it bothers me that there are two gradle modules that I have to maintain. It also makes it a bit clunky when I need to decide where to put class files (in real or in stub). I should be able to just have a single library and somehow switch between the two types using gradle "configurations
https://docs.gradle.org/current/dsl/org.gradle.api.artifacts.Configuration.html
However, I haven't been able to learn how to do this yet. I tried creating the "configurations" block in my android library as such
configurations {
real
stub
}
and then in my app's build.gradle, do the following.
dependencies {
flavorOneCompile project(path ":myLib", configuration "real")
flavorTwoCompile project(path ":myLib", configuration "stub")
}
This doesn't seem to work though. Is there a way to achieve what I want? That is, have a single library so that my source code for my library is self-contained, but also provide different variations of my library?
Thanks!

Related

How to organize common gradle tasks and extract common code blocks

I have an android project consisting of 10+ library modules. Each of these modules contains a lot of gradle code common to all modules. For example, I'm using the Javadoc generation task from this answer in all of my build.gradle files.
How would I go about to extract that task creation logic to a function put in a separate gradle file which can be "included" by each module? The code is identical for all my modules, but obviously depends on variant and project. Is it possible to extract a function that takes a project as parameter and returns a task for that project?
I'm probably going around this backwards since I really suck at Gradle but any pointers leading to me avoiding having the same 60 lines of gradle code in 10 different files (lib1/build.gradle, lib2/build.gradle, ...) would be helpful!
In fact, taking it even further, basically my entire build.gradle is identical for all projects except the dependencies section - there's an android block with buildTypes, compileOptions etc, there are some plugins applied (apply plugin: 'com.android.library' etc), and there are some artifact parameters set up. In fact, only my dependencies differ at all. So I'm wondering if it would be possible to extract a common file completely, like this (pseudo code obviously):
<include common.gradle> // includes android block, common tasks, artifact setup etc.
dependencies {
api 'androidx.appcompat:appcompat:1.1.0'
api 'com.google.code.gson:gson:2.8.5'
...
}
You can extract your common settings into a gradle file (lets say common.gradle and then use it as
apply from: '../path/to/common.gradle'
Reference: https://docs.gradle.org/current/userguide/plugins.html#sec:script_plugins
The recommended way of extracting common build logic is to leverage buildSrc and create plugins that are then applied to your projects.
This has a number of benefits of applying a script, as documented.
In addition, I would recommend being explicit through plugin application. While you could develop a single plugin that configures all your projects, with some logic based on project name for example, it is usually better to have different plugins matching the different project types.
That way, when opening a build file you immediately see what kind of project it is.
And of course you can have common code in buildSrc for the common parts.
One more benefit is that moving from a buildSrc plugin to a published plugin is much easier ... just in case your configuration patterns start to appear in different projects.

Kotlin common library to reuse in multiple MPP

I'm setting up a Kotlin multiplatform project so I can reuse common code in multiple platforms for a single app. While building the common code for the app, I've extracted some base classes that I'd like to be able to reuse as a library in multiple multiplatform projects. I'm trying to add the library as a dependency in commonMain. There are a couple of things I don't understand.
First of all: is this currently possible?
If yes:
The default stdlib-common is a jar file, correct? How come a jar can be referenced as a dependency in commonMain if no Java can be used there? Or is it okay to use a jar compiled from pure Kotlin, as long as it only has Kotlin dependencies?
How do I compile a pure Kotlin jar that can be used in commonMain the same way as stdlib-common is used? Are there any sample build.gradle projects or guides for how this should be packaged?
If no:
What options do I otherwise have to reuse code over multiple multiplatform projects, if I want to avoid duplication? Do I actually need to keep all source within the actual commonMain source folder? Can it be linked from another folder if so? I tried adding additional content roots but it didn't seem to work since Gradle controls the configuration and I'm not sure how to add additional content roots in commonMain through Gradle.
Thanks in advance.
I got it working, mainly from looking through this thread and looking at this example. Although some of it might be dated by now, it helped me understand the following:
MPP1 can have another MPP2 as a dependency. Here is a list of MPP libraries for reference.
MPP2 needs to generate artifacts for the same set of platforms as it is used in by MPP1.
MPP2 generates platform artifacts along with a module file where they are described. MPP1 can then use the below configuration. Thanks to the module file, it's not required to explicitly add each platform's corresponding dependency, the dependency only needs to be declared in commonMain.
commonMain {
dependencies {
implementation kotlin('stdlib-common')
implementation 'com.company:mpp2:1.0'
}
}

Share test classes between modules

I would like to be able to have common test code in a library module of my android projects, so that the different apps in the project can use them.
The problem is, apps cannot import classes from <library>/src/androidTest/java, and if I move that at code into src\main, it would have to move dependencies from androidTestCompile to compile (more dependencies to the release build).
The only solution right now is to create a separate library to hold the shared test classes, however this has the downside of adding a new library to the project structure, which is not that big a deal, but I'd like to know nonetheless if there are better solutions.
I'd rather implement a Gradle hack at this point if any Gradle (Android plugin) wizards out there can help me find one.
Since I got no answers, I might as well answer my own question.
I ended up using the solution I already mentioned in my question:
Create a library with shared test classes (not actual test cases, but common code to be used in the final ones) under src/main and import it with androidTestCompile or androidTestImplementation for recent gradle versions.
Got a solution that doesn't involve adding a module? I won't accept my own answer since it doesn't exactly answer the question.
If you have code (test or otherwise) that can be reused across multiple modules, the appropriate thing to do is exactly what you've done: put it in it's own module and import it into the other modules.
The small overhead of creating a new module is giving you a lot of power. It allows you to manage its build without having to change every dependent module's build.
Any other option I've tried (creating a single module that contains all modules' tests, e.g.) ends up being a much bigger headache and a dependency nightmare.

What dependencies should one be putting in each module of an instant app?

I'm in the process of writing an instant app for others to learn how to write an instant app and hoping to get some ideas on how to best structure the app for dependencies.
Now reading the Android developer docs on project structure and referencing the diagram below:
I'm wondering with the new gradle 3.0 dependency configurations, what libraries should live in which modules?
Base Feature
I was thinking pretty much anything in the base feature module should be using the api gradle configuration since the base feature module essentially compiles down to an AAR library file. One question I might have for this module, if one was to use ROOM would this be the module to place it in?
Feature
Now in the feature modules, it is my understanding that everything should be utilizing the implementation gradle configuration since these modules should not leak there dependencies out to any other modules in order to truly make them independent from one another.
Just looking for some confirmation of my understanding and also any ideas to help with the project. Here is the github repo if you want to check out the code I got so far. It is really simple at the moment, but I was thinking about messing around with the Star Wars API using Retrofit.
Thanks for any help and gladly accept any contributions if you want to to try and make a pull request yourself for any other concepts in making an instant app that others should know.
Shared details in your question are correct. Consider some of the below suggestions which add to the points mentioned by TWL:
Adding certain libraries to specific feature module which should
be included in the feature module only, instead of being added in the
base APK.
For example, let's say you have an application that depends on
libraries X, Y, and Z. Initially, you may pack all the libraries in
the base module by placing all the dependencies in the base
gradle.build file. But if only the code in the feature module requires
library Z, it makes sense to move that dependency from the base module
to the feature module.This works as long as no other feature modules
depend on the same library. If multiple feature modules use the same
library it definitely makes sense to keep it in the base module.
Taking care of Transitive dependencies.
Transitive dependencies occur when the library your project relies
upon depends on another library, which in turn may depend on yet
another library. Sometimes those transitive dependencies may contain
unexpected surprises such as libraries you do not need at all (i.e. a
JSON processing library you never use in your code.)
I hope this adds some information to your query,
I'm wondering with the new gradle 3.0 dependency configurations, what libraries should live in which modules?
Some of these links can also be referred for additional data:
Android Instant Apps(best-practices)
AIA structure
As mentioned by keyboardsurfer, your dependency assumption is in the right direction.
Base is at the root and acts like a library shared by all the
non-base feature modules, so its shared dependencies should be set with
api so that modules that depend on it can also access them. (though, base doesn't have to act only like a library, it can
also be a feature APK itself)
Features, as an instant app, each one extends out to the end as its own APK, so there's no reason it should be leaking its dependencies to any other modules and therefore dependencies should be set with implementation here.
Within the Google Samples, the cookie-api and install-api are some samples that more clearly demonstrate the dependency configuration usage as how I explained above.

Gradle for Android, AARs, and Conditional Dependencies

Short Form: What are some ways of organizing the code/POM for an AAR, such that apps using that AAR only have dependencies they actually need?
Long Form:
Suppose we have an app that depends upon an Android library project packaged as an AAR (L).
L contains a mix of classes, and any given app (like A) will only use a subset of those classes. For example:
L may contain Fragment implementations for native API Level 11 fragments, the backported fragments, and ActionBarSherlock-flavored fragments
L may contain Activity implementations for regular activities, FragmentActivity, ActionBarActivity, and ActionBarSherlock-flavored activities
L may raise events via LocalBroadcastManager, Square's Otto, and greenrobot's EventBus
And so on
These cases have two main commonalities, as I see it:
Apps will usually only care about some of the classes. For example, an app that uses Otto will not care about code that references greenrobot's EventBus, or an app that uses ActionBarActivity will not care about ActionBarSherlock.
If the AAR is an artifact in a repo, apps will not care about all possible upstream dependencies that is needed to build the AAR. For example, an app that is using native API Level 11 fragments will not need support-v4 or actionbarsherlock, even though the AAR itself needs them to build the AAR
If we were to go with JARs instead of AARs and dump dependency management, this is fairly straightforward. Building the JAR would have compile-time dependencies (e.g., support-v4). However, apps using that JAR could skip those dependencies, and so long as those apps do not use classes that truly need those dependencies, life is good.
However, I am having difficulty in seeing how to accomplish the same thing with AARs and Maven artifacts specified in a build.gradle file. If L has a dependencies block referencing the upstream dependencies, apps will in turn download those dependencies transitively when the apps depend upon L.
One solution that I am fairly sure will work is to split L into several libraries. For example, using the fragment scenario, we could have:
L1, which contains the implementation for the native API Level 11 version of fragments, plus any common code needed for other scenarios. This library would have no upstream dependencies.
L2, which contains the implementation that uses the Android Support package's backport of fragments. L2 would have dependencies on L1 and on support-v4.
L3, which contains the implementation that uses Sherlock-flavored fragments. L3 would have dependencies on L1 and actionbarsherlock.
Then, an app would choose whether to depend upon L1, L2, or L3, and therefore would only get the necessary upstream dependencies (if any).
My question is: is that the best solution? Or is there something else in the world of Gradle for Android, AARs, and Maven-style artifacts that would allow apps to depend upon a single L? I am concerned about possible combinatoric explosions of libraries to handle a varied mix of upstream dependencies. I am also concerned about oddball apps that actually do need multiple implementations and whether or not we can reliably specify the dependencies for those (e.g., an app depending on both L1 and L2, because that's what that app's author thinks that app needs).
I know that there are ways for an app to block exclude dependencies (see Joseph Earl's answer for the syntax), so an app could depend upon L but then block the actionbarsherlock upstream dependency if it is not needed. While that could work, for cases where I'm the author of L, I'd rather go the L1/L2/L3 approach, as that seems cleaner.
Any other suggestions?
I'm not aware of any dependency management feature that would help with this.
Having a single libraries with many dependencies and the ability to remove unwanted dependencies could work but there are some issues that will come with this:
You have to rely on users of L to remove the right dependencies.
You'll have classes in the library that won't be easy to strip out with Proguard. Proguard will not remove anything that extends Fragment/Activity/etc, and L should provide a proguard rule files to not remove classes that extend the support Fragment/Activity/etc... This will make it difficult to remove unwanted classes.
Some implementation may have additional resources and right now we can't strip out unneeded resources.
I think splitting the library is the right thing to do, however this is going to be complicated to do until we have flavors in library projects. Once we have this I think it'll be much easier to handle. You wouldn't have a base L1 and L2/L3 extending it. Instead you'd have a single library that generate different variants, each with their own dependencies.
With flavor dimensions, you could handle support libs vs event libs combinations though you'd definitively get some explosions if you add more dimensions.
The advantages of a multi-variant library is that it's much easier to share code across a subset of variants without introducing even more sub libraries. You can also have bi-directional references between the main code and the flavors, which you couldn't have with L2/L3 depending on L1.
As for supporting Ant/Eclipse, this is definitively going to be complicated. I would ignore Ant definitively. If someone only cares about command line build they should already move to Gradle.
As for Eclipse, well we'll have Gradle support at some point, but I understand if you can't wait.
In your application project can exclude transitive dependencies in Gradle (e.g. exclude ActionBarSherlock being included if you know it won't be used), e.g.
dependencies {
compile('org.hibernate:hibernate:3.1') {
// Excluding a particular transitive dependency:
exclude module: 'cglib' //by artifact name
exclude group: 'org.jmock' //by group
exclude group: 'org.unwanted', module: 'iAmBuggy' //by both name and group
}
}
You can also ProGuard (and DexGuard) to compact and strip out unused code.
Gradle doesn't yet support marking dependencies as optional as Maven does (which could be used in the library project).
Well, the first thing I'd like to mention is Gradle configurations that are actually kinda reflection of Ivy configurations: a module you deploy to some repository can have different configurations and each configuration may have its own set of dependencies.
http://www.gradle.org/docs/current/userguide/dependency_management.html#ssub:multi_artifact_dependencies
Yet, Maven does not fit this case, since there are no configurations in Maven world. And artifact classifiers will not allow you to have different dependencies.
What we can do with Maven is remove compile time dependency declarations from POM or mark them with provided scope.
AFAIK ability to specify compile-time only dependencies will be available in future releases of Android Gradle plugin.
https://code.google.com/p/android/issues/detail?id=58626

Categories

Resources