androidgradleandroid-library

How to do android lib from part of multimodule app


Create lib from android app screen

My current task is to separate one of the application screens as a library so that another application can have it and keep all the functionality of the screen

The screen consists of 3 modules of clean architecture: Ui, Domain & Data and it works fine in the application. Also, we have some utills modules in UI & Data layers.
Our modules have the com.android.library plugin

How can I create such feature as a library without changing the current architecture of the project (i.e. keep the current modules intact)

And I also think that there will be problems combining the DI code of the library and the client code. (We use Koin). But now it is not big problem.

Project look like

root
|-- app
    |-- build.gradle
    |-- src
        |-- CombineFeatures
|-- ui
    |-- feature1
        |-- build.gradle
        |-- src
            |-- Feature1UICode
|-- domain
    |-- feature1
        |-- build.gradle
        |-- src
            |-- Feature1DomainCode
|-- data
    |-- feature1
        |-- build.gradle
        |-- src
            |-- Feature1DataCode
|-- settings.gradle

Attempts

I tried to build the AAR library from the UI module using the gradle assemble task, however, it did not have the rest of the clean layers and denepndecies from project (like material3 etc.) , which is why I got java.lang.NoClassDefFoundError: Failed resolution of....
In this case, I could only invoke the functions of the imported module.

Can I combine several AAR files into one? As variant, I can try use fat-aar-lib -> Building aar library from multimodule project. But it seems like not modern solution.

We do not consider the option of moving the functionality to a separate project and repository, because it will be difficult to maintain in future

Also, earlier we tried to make composite builds, but it was very slow and forced us to have all the source code (of the project) locally, so we abandoned this option


Solution

  • After some research I found several solutions to this problem:

    1. Use gradle plugins like fat-aar, however most of them are currently (07.2024) unsupported or don't give the necessary flexibility
    2. Make each module of the application a part of one big library
    3. Gradle Composite Builds but it was very slow and forced us to have all the source code (of the projects) locally, so we abandoned this option

    As a prod solution, option 2 was preferred, because it would allow us to connect individual functions without depending on other unnecessary ones


    For this purpose, I wrote a small plugin for publishing in buildSrc

    plugins {
        `maven-publish`
    }
    
    afterEvaluate {
        publishing {
            publications {
                register<MavenPublication>("release") {
                    from(components.findByName("release"))
    
                    groupId = project.hierarchy.concatGroup()
                    artifactId = project.name
                    version = libs.versions.versionName.get()
                }
            }
            repositories {
                maven("...") {
                    credentials {
                        username = getLocalProperty("MAVEN_USERNAME")
                        password = getLocalProperty("MAVEN_PASSWORD")
                    }
                }
            }
        }
    }
    

    And some utils in buildSrc

    val Project.hierarchy get(): List<Project> =
        generateSequence(this, Project::getParent).toList().reversed()
    
    fun List<Project>.concatGroup(): String {
        return dropLast(1).joinToString(".") { it.name }
    }
    

    Thus, we get the name of the dependencies in the repository the same as the name of the modules tree of your project

    And don't forget to connect your maven repository to the gradle project

    Then you can connect this plugin to the modules you want to put into the repository (or another plugin as in my case)

    plugins {
        id("com.android.library")
        kotlin("android")
        /* ... */
        id("publishing-plugin")
    }
    

    After that, you can call the publishAndroidReleasePublicationToMavenRepository or publishReleasePublicationToMavenRepository task, depending on the configuration of your project (KMP or Android).

    After that, all modules to which the plugin has been applied will be uploaded to the specified maven repository.


    The client can then download any module as a dependency, and that in turn will download all other modules required for correct work

    your-app-feature = { group = "YourProject.features.feature", name = "feature", version.ref = "your-proj-version" }
    

    Advantages:

    Minuses:


    upd:

    After a year from the previously found solution.

    I Found an article on a Russian resource on this topic using a more modern version of the fat-aar solution - Grease

    It may be worth considering switching to such a solution