While you're converting your app to a Maven project, you'll soon realise that you need to tackle any shared libraries first. If you've got more than a couple of apps, it's likely you've created a common library between them to keep your code DRY (don't repeat yourself).
(Sorry for interlinked Part 3 and Part 4, but it's just the nature of this process)
Contents
The problem with Maven (and Gradle)
The way Maven works is it downloads shared libraries from a repository of code and attaches it to your code when needed. The problem with privately shared libraries is that you need to push it to a place that Maven can access.
A few answers on StackOverflow say you have to push your private library to a public Maven repository. To me, that's just not acceptable! A workflow should not force private code to be accessible to the public. Not to mention you'll clutter up public repos with code that should not be there in the first place and waste good namespaces. In short no, this is a shit solution.
A other sources suggested making a local repository. That sounds much more reasonable but how do I make that available across multiple dev machines?
Using Google's Support library from the Android SDK local repository
Most likely you're using Google's Android support libraries, so you'll also need to include the SDK local repository as a source for Maven.
First of all, make sure you have the files in the first place.
- Fire up the Android SDK Manager
- Scroll down to Extras and make sure "Android Support Repository" is ticked
- Click Install
To include the local repository as a source, paste this under the <project> element.
<project ...>
<repositories>
<!-- Include Android Support Repository so we can access support-v4 and appcompat-v7 aar -->
<repository>
<id>google-sdk-support</id>
<url>file://D:/Android/sdk/extras/android/m2repository/</url>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>false</enabled></snapshots>
</repository>
</repositories>
</project>
As you can see in the <url>element, the value can also be a local file system. If we can somehow replicate that structure we can safely share our code, privately.
To gain some inspiration, I studied the local Maven repository found in the Android SDK under "android-sdk\extras\android\m2repository\" and found an answer!
Setting up a local repository for your library
Knowing that Google can make their libraries accessible locally, I decided to make my own library accessible to other projects using this local repository method.
The only difference is that I'm creating a folder called "local-repo" within the root of the library project. This way, projects that use this shared library can refer to it using relative paths.
Initially I tried using the exec-maven plugin to manually move the main artefact to a common folder, but it just wouldn't work from within the Eclipse IDE. It'd work from the command line, but it's too much work compared to install:install-file" which does just that with far less configuration. Gotta use the right tool for the right job!
So I set up the goals so it'd run "package" to first create the APK, and then using the "install:install-file" goal from the Maven Install plugin, pushes the main artefact (AAR, JAR, etc) into the local-repo repository in a structure which Maven can use.
I stored this command into "mvn-update-local-repo.bat" (excuse the poor naming) in the root folder of the project and run it whenever I need to push something to the local repo.
mvn package install:install-file -DlocalRepositoryPath=local-repo
-DcreateChecksum=true -Dpackaging=aar
-Dfile=target\twig-android-library.aar
-DgroupId=org.twig.common -DartifactId=twig-android-library
-Dversion=1.0.0-SNAPSHOT
The script pushes the file target\twig-android-library.aar to the repository "local-repo". It specifies that it's v1.0.0-SNAPSHOT.
This should be made more flexible by reading the pom.xml file, but haven't found a way yet.
Snapshot builds and caching
Snapshot builds are fetched only once a day. If you're testing and making multiple changes in the same day, use the -U arg to any mvn command to force Maven to fetch from the source, rather than once a day.
Why is this important?
Because each time you build your APK project, the shared library's AAR files from your local-repo are automatically cached in %USERPROFILE%\.m2\repository (aka ~\.m2\repository).
If fixing bugs in your library, Maven is going to use the cached copy so your changes won't be compiled into the APK unless you specify the -U option.
Using your shared library from an app project
So now you're ready to use the shared library in your APK project.
Edit your pom.xml file and look under <projects><repositories>.
Add another <repository> entry for your shared library.
This example assumes that your app project and shared library projects are on the same folder level.
<repository>
<id>twig-android-library</id>
<releases><enabled>true</enabled></releases>
<snapshots><enabled>true</enabled></snapshots>
<url>${project.baseUri}../twig-android-library/local-repo</url>
</repository>
And there you have it, we've safely passed another hurdle without breaking any monitors.
Now you're ready to debug!
Figuring out the cryptic ClassNotFoundException: Didn't find class on path: DexPathList [zip file], nativeLibraryDirectories=[ /vendor/lib, /system/lib]
Fortunately this is a simple one. It's due to the incompatible versioning of libraries used between the Android Support Library and the Android Support v7 Compat library.
Make sure the support library project you've associated with your APK project is the same version as the support library you declared in the pom.xml dependencies.
This should clear you of any more cryptic errors.
Sources