I am working to build an SDK that can be used in other application.
My project structure is as follows:
ProjectFolder
|
+--AndroidLibs
| |
| +--UI (android library - AAR)
| |
| +--Protocol (android library - AAR)
| |
| +--infra (android library - AAR)
|
+--SDK(depends on UI, Protocol and Infra)
|
+--APP(depends on SDK)
As you can see, we have 3 different libraries that we work on, each one is a module in our system (infra, ui and protocol). Each one of them is creating an AAR.
Our SDK is a wrapper with some API calls to the lower layers.
We want to create one AAR that depends on all other AARs, but from some reason when we tried to run it, it says that he can't find the source code for some classes.
I found some questions related to this issue, but they didn't worked.
Also tried to work with transitive dependencies, but the bottom line is the same - can't find the source code.
android-studio-how-to-package-single-aar-from-multiple-library-projects: One answer that says there no solution for that (Google employee).
create-an-aar-with-multiple-aars-jars: use the transitive dependencies.
Create Mojo for creating AAR with all "aar" dependencies
Is there anything else we can do?
From my answer here:
As far as I know you cannot include aars inside an aar. They don't have configuration files that state what dependencies they need. You can either
Strip the source code from the libraries you are using and compile it with your aar. This will do the job if the UI/Protocal/Infra libraries are in-house and you are the only provider.
Consider uploading to bintray or Maven Central Repository
Number two is more preferable since this way all your client has to do is to include a link such as compile 'com.abc.efg:version' to grab all the dependencies you configured. It is also a much better option because there are ways of dealing with version conflicts (ex. with exclude group).
Imagine if your client was using another sdk which was pulling in a different version of UI/Protocal/Infra. If your aar was given to them via the first method, they won't even be able to build the project at all due to version conflicts. However with the second version, they can simply do
compile ('com.abc.efg:version') { exclude group: 'com.companyName.ui' }
and be free from all that headache. A real life example is Facebook's SDK. It pulls in google's play-services, but people often already include that as a dependency for their project and run into problems like this.
As your projects generally relies on both internal and third-party libraries. The internal libraries can be published on Artifactory repositories and resolve the dependencies over Artifactory with Gradle.
Its easy! Just go through the below articles,
http://jeroenmols.com/blog/2015/08/06/artifactory/
https://inthecheesefactory.com/blog/how-to-setup-private-maven-repository/en
This is highly scalable and its easy to maintain your code across multiple modules.
Hope this would help you!
Related
How can I access Activity from AAR library which is not directly included into the project but is embed to another AAR library?
I got an error java.lang.NoClassDefFoundError: Failed resolution of:
The class is public and if I compile it directly in project application it can be used without problem.
I included an AAR to my project like this :
ProjectApplication
|
+--sharedModule (android library - AAR or any working solution)
| |
| +--Module1 (android library - AAR or any working solution)
| |
| +--Module2 (android library - AAR or any working solution)
| |
| +--Module3 (android library - AAR or any working solution)
compile (project(":sharedFrameWork")){ transitive = true }
which has also included in itself 2 other AAR libs. They are also set to be Transitive. When I try to open an activity from one of the sub AAR libraries. I got the class not found error. But when I include that particular AAR into my application directly the class is found and can be used. It looks like I do not have access to any sub AAR libraries which are not included directly into my Application.
To better describe my situation :
I have to create an integration AAR library (later called 'sharedFrameWork') which includes multiple AAR libraries and is later embed into an application.
Multiple AAR -> Shared AAR 'sharedFrameWork' -> Application
The sharedFrameWork has some method which starts some activities from the included AAR's or set up basic communication with the server. I have read that if all the dependencies are set to be transitive it will make it work, but unfortunately it does not.
So When I call from my application a method which should start an activity from one of the included AAR in sharedFrameWork the app reports me that no such a class was found.
But when I include that AAR module right to my application not to sharedFrameWork, and then call the exact same functionality the Class is found and the project is working as it is designed. Can you help me how can I create this sharedFrameWork to be working as it is designed? Can it be done by using AAR or should I take another approach? If any other way it can be done and the result will be that I can deliver just one library and it will work as designed so it can access its submodules I will go with it, feel free to point me out the best approach for this problem.
If I get it right, you want to create a fat AAR and achieve a single import of several libraries.
Now, since you haven't provided more info, I'm going to assume you are using gradle 2. In gradle 2, submodules don't share their dependencies. neither does anything you put into the libs folder. So, first, I would upgrade your projects to gradle 3, switch from "compile" command to "api" command, and check.
If that does not work, the next step would be to apply the gradle maven plugin to each one of your modules, and deploy the resultant AAR file to either your local maven repo (automatically created when you install maven), or a remote repo, like jitpack. If you have AARs/JARs into the libs folder, deploy them to a repo too and import them from there (libs folder scope is local in gradle 2, and in general, is a bad idea to use it instead of a centralized repo. You can even use github as a repo). Then, use the artifacts.
Finally, the last solution for your problem would be to use "shading"; the process to pack several different artifacts into one. If you can't upgrade to gradle 3, or deploy the artifacts somewhere (unlikely), this is what you should do. There are several plugins for this:
https://plugins.gradle.org/search?term=shade
https://github.com/zawn/android-shade-plugin
if those don't work for you, switch to maven and use the maven shade plugin.
As a side note, you should not provide a fat AAR. Is better to keep your framework in separate modules. That will speed up your build process and allow you to save space if you don't require some classes. Even in a multimodule project, you can create separate artifacts, one for each module, and import them as you need. Just avoid circular references (a module A that requires a module B which requires module C which requires module A) and you'll be fine.
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 was wondering what is the recommended way of including library projects in an Android Studios gradle like this:
First way:
compile 'de.greenrobot:greendao:2.1.0'
Second way:
compile files('libs/greendao-2.0.0.jar')
and what are the pros and cons.
In the first case gradle is completely handling the process of dependency management, i.e. downloading the jar and including it in your project.
In the second case you have to manually download the jar and include it in the libs folder.
The simpler and preferred way is the first.
First way: compile 'de.greenrobot:greendao:2.1.0'
PRO:
Easy, quick to update
CON:
Internet connection required when updating your gradle file
Second way: compile files('libs/greendao-2.0.0.jar')
PRO:
You can make changes to the library and those won't be overwritten.
Like Qian Sijianhao said, it's quicker to build.
CON:
More work to set up, update
In most cases I think you want to go with the first way.
Trust me, the second way will save lots of your building time.
By the way , time is money.
In the first way, i.e:
compile 'de.greenrobot:greendao:2.1.0'
compile 'com.library.sample:library:x.x.x'
Gradle will finding these dependencies, and making them available in your build. If your dependencies have a dependencies, gradle will also finding them and include it for the project. So you don't need to manually add all the dependencies.
Quoting from Gradle documentation:
7.1. What is dependency management?
Very roughly, dependency management is made up of two pieces. Firstly,
Gradle needs to know about the things that your project needs to build
or run, in order to find them. We call these incoming files the
dependencies of the project. Secondly, Gradle needs to build and
upload the things that your project produces. We call these outgoing
files the publications of the project. Let's look at these two pieces
in more detail:
Most projects are not completely self-contained. They need files built
by other projects in order to be compiled or tested and so on. For
example, in order to use Hibernate in my project, I need to include
some Hibernate jars in the classpath when I compile my source. To run
my tests, I might also need to include some additional jars in the
test classpath, such as a particular JDBC driver or the Ehcache jars.
These incoming files form the dependencies of the project. Gradle
allows you to tell it what the dependencies of your project are, so
that it can take care of finding these dependencies, and making them
available in your build. The dependencies might need to be downloaded
from a remote Maven or Ivy repository, or located in a local
directory, or may need to be built by another project in the same
multi-project build. We call this process dependency resolution.
Note that this feature provides a major advantage over Ant. With Ant,
you only have the ability to specify absolute or relative paths to
specific jars to load. With Gradle, you simply declare the “names” of
your dependencies, and other layers determine where to get those
dependencies from. You can get similar behavior from Ant by adding
Apache Ivy, but Gradle does it better.
Often, the dependencies of a project will themselves have
dependencies. For example, Hibernate core requires several other
libraries to be present on the classpath with it runs. So, when Gradle
runs the tests for your project, it also needs to find these
dependencies and make them available. We call these transitive
dependencies.
Gradle will store the downloaded library to your USER_HOME/.gradle. In Linux, it will store it in /home/user/.gradle/caches/modules-2/. in Mac it will store it in ~/.gradle/caches/modules-2/.
By the way, if you have used the library, you can set Android Studio to use a local cache of the library.
In the second way, you need to manually add the library for your project. And you also need to include all the remaining dependencies of the library. This is so error prone.
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 :).
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