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.
Related
I have a modularized application architecture with 3 module layers.
A core library module at the bottom and an application library module and my application on top of it. Each builds it's own native shared library. But not only the java code but also the C++ code in them also depends on each other.
So my "application.so" must link to "library.so" and "core.so" and on the other hand "library.so" must link "core.so" just like you expect from any layered architecture.
I can see in the file system that the shared so libraries all end up in the same build directory but i can't find a way to make them link each other. "find_library" is not going to work. And just naming them in "target_link_libraries" is not going to work either.
EDIT: With
buildFeatures {
prefabPublishing true
}
prefab {
infosqueezer {
headers "src/main/cpp/include"
}
}
i was able to generate an aar. But i'm not able to include it into the the other module. Both debug and release aar's are generated in the "library/build/output/aar/" directory. The modules are in the same project so i just need to reference by file somehow.
I'm using
implementation project(path: ':library')
but it does not pickup the so file. Also the AAR does not contain any "libraryConfig.cmake" or "library-config.cmake" that the find_package command would need to find the link library.
Also the whole prefab process seems to be terrible. I just want to split the in project source code to reduce compile time and dependencies. Prefab is adding so much and surely a good idea to distribute indpendent libraries but overkill for internal libraries.
All i need is just a way to reference the generated "library.so" file in another module.
I know it's Android but do they really make everything so terrible complicated?
There is no supported way to depend on native libraries from other modules in the same project with externalNativeBuild (at least in part because it will lead to worse build performance, see below).
As described your project should have a single gradle module for its native code if you want the best build performance (and want to be on a supported usage model).
If you really want these to be separate, the only way to do this that works with AGP is for them to be separate projects. I don't think you should do that.
Also the whole prefab process seems to be terrible.
This isn't what it's meant for. Screw drivers make terrible hammers.
I just want to split the in project source code to reduce compile time and dependencies.
Splitting gradle projects into more modules usually makes compile times worse, not better. I can't think of any reason that it would improve build performance for native projects, and it can easily make it much slower since invoking CMake is not cheap and splitting modules up multiplies that cost.
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.
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!
We have developed an android library (aar) using Java and it depends on android support library v4. In fact, we have extended ViewPager (android.support.v4.view.ViewPager) class and consumed it in our library’s GUI.
We have applied proguard on the library and it works fine with our test apps but we have a customer that has developed its android app in native C++ and they are going to integrate our component into their app and there is an issue on build.
They receive com.android.dex.DexIndexOverflowException on build which is a sign of having more than almost 64K methods that are allowed in a single dex.
They asked us to use fewer or smaller dependencies as they have to include our component dependencies into their build setting and one of their suggestions is that we should extract ViewPager out of android support source and put it in our component source code.
Now the questions are
If we extract ViewPager out of android source and add it to our
library source code then will it reduce the amount of methods to
prevent mentioned exception? Is it a good practice to do that?
What is the best way to resolve this issue?
Thanks
If we extract ViewPager out of android source and add it to our library source code then will it reduce the amount of methods to prevent mentioned exception?
Probably not, at least for release builds. Your customer should have configured ProGuard, which will identify and remove unnecessary code pulled in via libraries.
Is it a good practice to do that?
No. Your customer should know better than that.
What is the best way to resolve this issue?
Mostly, it is not your problem. It is your customer's problem. Your customer is blaming you, but there is no evidence in your question that your library is a significant source of the customer's DEX method references.
Your customer should be configuring ProGuard, and your customer should use the APK Analyzer in Android Studio 2.2+ to see where their method references come from.
You, in your library, could:
Try to use more focused dependencies than support-v4. ViewPager itself is in the support-core-ui artifact. However, if you are using FragmentPagerAdapter or FragmentStatePagerAdapter, in addition you will need either support-fragment (if you are using the v4 edition of those classes) or support-v13 (if you are using the v13 edition of those classes).
Use the APK Analyzer on some demo project that you create that uses your library, so you can see how many DEX method references that your library uses, to see if you are really a significant source of such references.
Suggest ProGuard settings for your customer that will keep required classes of yours, to help them use ProGuard effectively with their app.
You can set jumboMode in your build.gradlefile like this:
dexOptions {
jumboMode true
}
This option will allow you to have 32it wide string references in your .dex file. Thus you can have 2^32 referenced strings in your project.
Hope it helps.
Available options are :
Yes, you can extract ViewPager from support library. ViewPager import some files from support library that are also required to be extracted. This will reduce method count with significant difference.
You can also use exclude parameter in dependencies in build.gradle file.
compile ('com.android.support:recyclerview-v7:+') {
exclude module: 'support-v4'
}
Ok, I realize that Gradle and Android Studio seem to think that all Library Applications are built for one project and one project only, but that is not the case. I have many shared Library Applications with common purposes that are shared throughout the organization. Gradle does not seem to be very accomodating to this desired solution. Can someone offer any insight?
My current Structure at a very rudimentary level is like this:
|--Directory
| |--PROJECT A
| |---Module 1
| |--Project B
| |---Module 2
| |--Project c
| |--Module 3
/////////////////////////////////////////////
My Current dependency structure is like this:
/////////////////////////////////////////////
Project A: (FYI, Builds Just Fine)
Project A's settings.gradle
include ':Module 1', ':Module 2'
project(':Module 2').projectDir = new File('../Project B/Module 2')
Module 1's build.gradle
dependencies {
compile project(':Module 2')
}
Project C: (FYI, BROKEN)
Project C's settings.gradle
include ':Module 3', ':Module 1'
project(':Module 1').projectDir = new File('../Project A/Module 1')
Module 3's build.gradle
dependencies {
compile project(':Module 1')
}
Breaks: Cannot resolve Module 2 inside of Module 1's build.gradle file.
This is because the directory structure for Module 2 is established inside Project A's settings.gradle so Project B has no idea where to render this from.
I understand that I can add
project(':Module 2').projectDir = new File('../Project B/Module 2')
to Project C and everything will work just fine. However Project C doesn't use or know about Module 2. I want other developers to have the freedom to use my common shared library project without having to dig in and see what library projects I used and include those in their settings as well. How can I specify my own dependency directory structure in the build.gradle instead of the settings.gradle to make it accessible to all that use it?
On a second note, but similar topic. I'm having the exact same issue with JAR files. If i specify a REPO in a Library Project's build.gradle like: myRepo1 and have a myJar1. Then when that library project is used in a parent project that doesn't define the repo that contains the jar in the library projects dependeny section, it fails to resolve the jar file from the library project when compile project(':libproject') is used. I have to duplicate the repo pointers in the parent's build.gradle file as well so that the libproject will build from the parent app. Any help on this one would be appreciated as well. As not every repo is used in every app so this can become redundant.
Ok this is a really old post, but still gets traction so let me update 3 years later since I originally wrote it lol.
Shout out to CommonWare who had the right best practice idea right from the start, but didn't provide an answer to mark up.
Let me start by saying that using project references like I was doing above should be limited to development stages only and should only be if the library project is also in development stage at the same time as the main project. Otherwise a dependency management server like Nexus, Apache Archiva, or S3 with Maven directory structure or equivalent would be preferred. I have learned many ways to manage dependencies since this, including transitive dependency management.
My preferred method is to deploy artifacts with POM files to Apache Archiva and then use these dependencies within the parent project instead of using relative paths to reference code projects now. This is the first choice.
However, if you are too new to dependency management and choose not to have a server for this purpose, you may package your AAR files or JAR files and put them in one centralized repo like artifact_repo and have everyone include that repo at the same folder structure and reference them relatively, but this is not good practice so I would steer clear if you can.
You can also take the artifacts and nest them in you libs directory and bring them in that way if you would like, but it becomes more of a manual update process which some people like and others do not.
Now this opens a whole different set of issues that you need to handle.
Transitive Dependencies and Child Repo pointers.
For example, if you wrapped your own Crash Reporting Library around Fabric or Hockey or other hoping to make it easy to trade libraries later, then you have found that the repo pointer has to live in the parent build.gradle files or the transitive dependencies are not found.
You could of course use one of those hacky Fat_AAR or Fat_JAR scripts that works "sometimes" until updated gradle then they break again until someone hacks it back together, but this is also poor practice as you are creating potential mismatch dependencies on support or other important child libraries and the "exclude transitives" only works if you are using pom files to control the transitives and not making the AAR or JAR file fat. So you are limiting your ability to control the dependencies.
So what i have finally come to terms with is that transitive dependencies should be managed through POM files to allow excluding or including without nesting into children libraries. Also libraries that require repo pointers inside of them, should probably not exist as they require parent boiler plate, introduce room for human error and typically don't save much time on wrapping analytics or crash libraries for example or you start getting into json configs that need to live in parent files for PUSH or other reasons. Just avoid it.
So long story short lol. Stick to dependency management tools they way they were intended to be used and you will be fine. It is when you are new to it or start getting hacky that you run into ugly code and ugly problems. Hope this encourages someone to do it the right way :)
One last thing :). I have recently started writing Gradle Plugins to manage my versions and dependencies as a separate file so that I can use intellisense to pull in dependencies and make sure all support, gms, and tool versions are the same across all projects. You can even copy down live templates with your plugin to enable intellisense for Gradle to work with your stuff. It's not too bad to do. Best of luck and happy Gradling :).