How to reuse the submodule in Gradle? - android

I have two following Android Studio project, structure like this:
projectA/
├----build.gradle
├----settings.gradle
├----bluewhale/
├----krill/
projectA settings.gradle file:include 'bluewhale', 'krill'
projectB/
├----build.gradle
├----settings.gradle
├----hello/
├----krill/
projectB settings.gradle file:include 'hello', 'krill'
You can see "projectA" and "projectB" contain the same module "krill". Actually, it's a library project.
My question is: how to reuse the submodule "krill" in Gradle? I don't want to include the same copy of "krill" in every project
Looking forward to your reply! Thanks!

If you have a sub-module that is used in multiple projects, you should think about extracting it to a separate project. Then you can create a dependency out of it and include it in both projects in dependencies section.
If you only use your local machine for development, without any custom repository, the best way would probably be to use the mavenLocal() repository. You can use the maven publish plugin to publish your jar into the local maven repository. It should be as simple as adding this to the new krill:
apply plugin: 'maven'
apply plugin: 'maven-publish'
publishing {
publications {
maven(MavenPublication) {
from components.java
artifact sourceJar {
classifier "sources"
}
}
}
}
repositories {
mavenLocal()
}
You might want to set the group and artifact ID. See the documentation for more info.
You can also keep krill in one of the projects, let's say ProjectA, if it has some relation to it. Then you set up the maven publishing in the krill sub-module. You can also publish to maven local by running gradle :krill:publishToMavenLocal and then use it as dependency in ProjectB.
Another option is to save the submodule outside the projectA and projectB trees and add it using something like this:
include("krill")
project(":krill") {
projectDir = new File("$settingsDir/../krill")
}
But I can't recommend this because it's hacky and your IDE might have a problem with it too.
Last thing that might be possible is to create symlinks from a directory where your krill project is located to both ProjectA and ProjectB. But that is a really bad idea e.g. when you are using a version control.

Finally I found an article here: an-alternative-multiproject-setup-for-android-studio. It works for me perfectly!
It shows us another Way different from Google’s Gradle Plugin user guide recommends
Sample code below: (Add this script to your project settings.gradle file)
include ':krill'
project(':krill').projectDir = new File('../otherProject/krill')
Project structure below:
RootFolder/
├----projectA/
│ ├----build.gradle
│ ├----settings.gradle
│ └----bluewhale/
│
├----projectB/
│ ├----build.gradle
│ ├----settings.gradle
│ └----hello/
│
└----otherProject/
├----krill/
│ └----build.gradle
│
└----otherModule/
└----build.gradle
For more details, visit gradle official document: Multi Project Builds

Related

Gradle unable to find internal dependency

Problem
I am developing an Android library. This library lives in a multi-module Gradle project with the following structure:
├── legacy
│ ├── src
│ │ └──...
│ └── build.gradle
├── data
│ ├── src
│ │ └──...
│ └── build.gradle
├── feature
│ ├── src
│ │ └──...
│ └── build.gradle
└── settings.gradle
data depends on legacy.
// data/build.gradle
implementation project(":legacy")
feature depends on data and legacy.
// feature/build.gradle
implementation project(":legacy")
implementation project(":data")
feature, data, and legacy are all Android library modules. feature is the published Android library. data and legacy are not used outside this project, and are therefore not published anywhere.
My Android application depends on the published feature AAR artifact.
implementation "feature:<version>" // package omitted for obfuscation
Gradle has no problem finding the feature library. However, Gradle throws an error because it wants to find data and legacy.
Execution failed for task ':mobile-app:mergeDevDebugNativeLibs'.
> Could not resolve all files for configuration ':mobile-app:devDebugRuntimeClasspath'.
> Could not find feature:data:unspecified.
Searched in the following locations:
- ... // omitted
Required by:
project :mobile-app > feature:<version>
I don't think it matters, but the mobile-app project is using a locking plugin, Nebula's Gradle Dependency Lock Plugin.
Attempted fixes
I'm a little confused why this error is happening at all. I expected the classes of legacy and data to be included in the feature AAR. That is clearly not the case. If that can be done, I prefer that solution.
Here are some dependency variations I've tried so far.
// feature/build.gradle
// doesn't work; same error
api project(":legacy")
api project(":data")
// doesn't work; mobile-app doesn't complain about missing artifacts, but fails to compile:
// cannot find symbol class <class in legacy>
compileOnly project(":legacy")
compileOnly project(":data")
I can successfully compile and run mobile-app if I use dependency substitution to replace the feature AAR with my local copy of the code repo.
// settings.gradle of mobile-app project
includeBuild('../feature') { // this is the local relative path to the project
dependencySubstitution {
substitute module('feature') using project(':feature')
}
}
However, this is not an appropriate solution to publish and subsequently enforce upon the entire dev team.
Gradle modules map 1:1, more or less, with Java/Android libraries.
Adding a dependency (implementation or api) to a library does not cause that dependency to get compiled into the library. Otherwise you would get class path collisions if you depended on two libraries which themselves depended on the same library as each other.
Instead, adding a dependency to a library adds that dependency to the library's POM and/or gradle.module file, which gets published along with the JAR or AAR to the repository. Consumers of the library (i.e. other Gradle or Maven projects, like your app) then parse that POM, and thus know which additional dependencies to download.
The way I see it, your options are:
Publish data and legacy to a repository your app can consume, just like feature. Your app won't consume them directly, but they'll be there for Gradle to download when it sees that feature requires them.
Move the code from data and legacy inside of feature (and mark it all as Kotlin internal or Java package-private to prevent it from being public API).
Refactor feature to publicly define all of its data sources as interfaces. Move the data and legacy modules into your app project and refactor them to provide the necessary concrete implementations of feature's required interfaces.

Gradle composite builds with custom repositories in Android Studio

Our company requires we have all our dependencies downloaded from our internal repository (Artifactory). Our Android project has the main Android modules, plus composite builds.
I was able to replicate the issue creating a simple project from Android Studio, and adding a composite build (or buildSrc) into it.
More or less like this (I didn't include the other files above to keep it simple):
project
├─ app (<-- application module)
├─ plugins (<-- composite build )
│ ├─ settings.gradle.kts (<-- settings for composite build)
├─ settings.gradle.kts (<-- settings for main project)
Both settings.gradle.kts (main + composite build projects) have something like this (again, leaving irrelevant elements out of it):
pluginManagement {
repositories{
maven(url="https://artifactory.mycompany.org")
}
}
dependencyResolutionManagement {
repositories{
maven(url="https://artifactory.mycompany.org")
}
}
This works perfectly while running from terminal (e.g. ./gradlew build), but the sync operation via Android Studio seems to always trigger dependencies download from *.gradle.org. After the sync, all the other project dependencies for build operations seem to work on Android Studio. If I create a project without composite builds (or buildSrc) it seems the issue doesn't happen.
This has been creating many issues, as you can imagine, but it doesn't seem that many people have been reporting the exact same experience. Apart from the issue of downloading dependencies from a different repository, it is also downloading these dependencies multiple times during the day.
I've also checked environment variables, Java SDK, etc to match between Android Studio and terminal, and Android Studio is also configured to use gradle from "gradle-wrapper.properties".
Has anyone faced similar issue, and has any clues on what else I could be missing? Or could that be an issue on the Android Studio gradle plugin?

importing an existing JAR or AAR as new project module

how to import JAR or AAR package as new project module in A new Android Studio Arctic Fox | 2020.3.1 Canary 9 ?
please let me know.
This works on Android Studio Arctic Fox Beta 02
Step 1 : Navigate to, File -> Project Structure. You can also press Ctrl+Alt+Shift+S
You will see a window just like below.
Step 2 : Click On app module as shown in image
Step 3 : Click on + icon as marked in image
Step 4 : You will see option to select jar/aar dependency. Click on it
You will see another window just like above asking you to specify path. Specify the path in which you kept the aar/jar file and hit Ok.
That should work
You can directly implement using JAR/ARR file path.
implementation files('/File Path/file.aar')
For Android Studio Bumblebee, original answer given here
I have followed steps suggested by the Android developer site:
Copy .aar file into the libs folder of the app
File -> Project Structure... -> Dependencies
Click on "+" icon and select JR/AAR Dependency and select app module
Add .aar file path in step 1.
Check your app’s build.gradle file to confirm a declaration.
Step 1: Put your aar file in the libs folder. And let’s take the file name is supernover.aar as an example.
Step 2: Put the following code in your Project level
build.gradle file,
allprojects {
repositories {
jcenter()
flatDir {
dirs 'libs'
}
}
}
and in the app level module write the below code,
dependencies {
Implementation(name:'supernover', ext:'aar')
}
Step 3: Then Click sync project with Gradle files.
If everything is working fine, then you will see library entry is made in build ->intermediates -> exploded-aar.
In my opinion, the best way to do this is to deploy the jar/aar to a local maven repository. if you install maven, you can use the mavenLocal() repository in gradle and read from there as with any other repo, regardless of the IDE you are using. All versions of Android Studio will work, all version of IntelliJ will work, VSCode will work, the command line will work, etc. Another advantage is, you'll be able to swap versions of the library as you do with all the others, just change the version in gradle (after deploying the new one), and will work for all your projects. Putting jars/aars manually into a project is just a bad practice, and reaaally outdated to top.
Once you installed maven, type this in your terminal:
mvn install:install-file -Dfile=d:\mylibrary-{version}.aar -DgroupId=com.example -DartifactId=mylibrary -Dversion={version} -Dpackaging=aar
Where you swap aar and jar depending on the type. The package name, group ID and library name are up to you, anything will work. I would use the library's package and name, and version 1.0 if you don`t have a version.
Here's an example link. Is old, but the process is the same. mvn install, then consume from mavenLocal().
For anyone in search of a solution still.
Create a new android Application project.
Convert new project into a standalone Library module.
Add maven-publish plugin to the module-level build.gradle
Connect your project to your Github repository (or create a new one).
In the module-level build.gradle, implement the Github Packages authentication flow. I'm using 'zuko' as an example - replace every instance of that name with your Github login.
android {
...
publishing {
repositories {
maven {
name = "GitHubPackages"
url = uri("https://maven.pkg.github.com/zuko/[git-repository]")
credentials {
username = 'zuko'
password = 'token' // this is a Git Personal Access Token
}
}
}
publications {
release(MavenPublication) {
groupId 'com.zuko.libraries'
artifactId 'choose-a-name'
version '1.0.0'
artifact("$buildDir/ogury-mediation-mopub-5.2.0.aar")
// you can actually put the artifact anywhere you want.
// This is the location of where you place your .aar file
}
}
}
...
}
If everything is connected properly, save your work, and run the the task: ./gradlew publish. The error logs are straightforward so just defer to the instructions and Google for more assistance.
To install a successfully published package into your desired project, use the same auth procedure for publishing.repositories, you don't need the second half, publishing.publications.
example: implementation 'com.zuko.libraries:choose-a-name:1.0.0'
You could configure a repository in you buildscript that looks for dependencies in a local directory
Use this to register a local directory as repository in your app module's build.gradle where libs is a directory under app module (<project>/app/libs/)
buildscript {
repositories {
flatDir { dirs 'libs' }
}
}
then declare your dependencies from the local file tree you registered earlier
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
}
This will include all jar/aar artifacts present under libs directory to be included in your module's dependencies.
PS: Local jar/aar artifacts will expect any transitive dependencies to be on the classpath unless they are fat-jars (package all transitive dependencies within the artifact), so these need to be added explicitly as dependencies.

How to add a class / an enum to settings gradle script on gradle 6.0+?

I want to add an enum called modules with the path of the sub module and some compilation types.
I used to have this in the buildSrc before gradle 6 and it was accessible in the settings.gradle
But from gradle 6.0, settings.gradle is compiled before buildSrc project. I have moved my enum to the settings.gradle, now it is not accessible to other project level gradle scripts.
The behaviour change is outlined in the below release notes.
https://docs.gradle.org/current/userguide/upgrading_version_5.html#changes_to_plugins_and_build_scripts
They suggest to add the enums / classes used in the settings.gradle to the build script closure, but I am not really sure how to do that.
https://docs.gradle.org/current/userguide/upgrading_version_5.html#plugins_and_classes_loaded_in_settings_scripts_are_visible_to_project_scripts_and_buildsrc
I've recently hit a similar issue, my company have custom code for authenticating with our Nexus which we were keeping in buildSrc. I can't turn this into a plugin since I'd need to store that in our Nexus and then would be in a catch-22 situation as I'd need to authenticate to get the authentication plugin!
I can see 2 potential workarounds for this:
Publicly published jar.
Build your custom classes as a separate jar, or a Gradle plugin if this fits the use case. Publish the jar to a maven repository that you can access from settings.gradle buildscript (for me this is difficult as it's sensitive company specific code).
This might look something like the following in your settings.gradle:
include "project-name"
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.companyname:gradle-utils:0.0.1'
}
}
Commit the binary
This isn't a desirable option but you can manage the source code for your custom buildSrc classes in another repository, then every time you make a change to them (hopefully infrequently) - you can build the new version and commit the built jar into the repositories that need to use it (perhaps under buildSrc). If your custom code has dependencies that aren't naturally on the classpath when gradle runs, you'd need to package these into the jar that you commit as well.
Your settings.gradle might then look like:
include "project-name"
buildscript {
dependencies {
classpath files("buildSrc/gradle-utils.jar")
}
}

Library projects in Android Studio only work if nested in the project tree?

I'm trying to build a project dependent on ActionbarSherlock in Android Studio.
After much reading I've ended up having a working build, but only if I nest the ABS project inside my main project like so:
MainProject
MainProject\ABS (copied here and then used import module)
MainProject\MainModule (the one created by the wizard)
I had to edit the various Gradle files, settings.gradle is as follows:
include ':OnTop'
include ':ActionbarSherlock'
I fixed the dependencies in the build.gradle files accordingly.
This is suboptimal, I think. Surely I don't want to replicate the ABS module in every new project that uses it? What should I be doing instead?
Every step are here : How do I add a library project to Android Studio?
If you want to more about gradle see the Google I/O conference by Xavier Ducrohet on youtube : http://www.youtube.com/watch?v=LCJAgPkpmR0
I searched this issue enough and finally these are my results:-
This sample project
-> project root
d:\asprojects\thisproject
-> module
d:\asprojects\thisproject\MyModule
-> libraries
d:\asprojects\lvl
d:\asprojects\MyLibrary
-> jars
D:/asprojects/android-support-v4.jar
D:/asprojects/GoogleAdMobAdsSdk-6.4.1.jar
-If you want to use external (outside the project) libraries then the settings.gradle has to point to it like so:-
settings.gradle
include ':lvl', ':MyLibrary', ':MyModule'
project(':MyLibrary').projectDir = new File('D:/asprojects/MyLibrary')
project(':lvl').projectDir = new File('D:/asprojects/lvl')
-Then you need to setup the build.gradle for the MyModule correctly like so:-
build.gradle
dependencies
{
compile files('D:/asprojects/android-support-v4.jar')
compile files('D:/asprojects/GoogleAdMobAdsSdk-6.4.1.jar')
compile project(':lvl') compile project(':MyLibrary')
}
-But the IDE will not like these absolute paths and will say not found.
-But gradlw from commandline work justs fine:-
gradlew clean
gradlew build
gradlew installRelease
etc

Categories

Resources