Best ways to organise your Android Dependencies 
Tags: android, gradle, dependencies, kotlin
Published: 2020-04-29 13:36:12

We all have worked or atleast seen a project with build.gradle file which looks something like this 

```groovy
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.0.3"
    implementation 'androidx.appcompat:appcompat:1.0.2'
    implementation 'androidx.core:core-ktx:1.0.2'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test:runner:1.2.0'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    kapt "androidx.lifecycle:lifecycle-compiler:2.1"
    implementation "androidx.room:room-runtime:1.3"
    kapt "androidx.room:room-compiler:$room_version"
    implementation "com.google.android.material:material:1.0.0-rc01"
    implementation "androidx.cardview:cardview:1.0.0"
    implementation "org.jetbrains.anko:anko-commons:0.10.4"
    implementation 'com.android.support:design:23.1.1'
    implementation 'com.jakewharton:butterknife:7.0.1'
    implementation 'com.jakewharton.timber:timber:4.1.0'
    implementation 'com.squareup.retrofit2:retrofit:2.0.0'
    testImplementation 'junit:junit:4.12'
    testImplementation "org.mockito:mockito-core:1.+"
```
Its very hard to find or update a dependency in this **Bloated** file, and this file will get worse as the development progresses.

#Fixing the mess  
###1. Naive Approach
We can clean up the mess by creating separate variable for the versions of dependencies used.
```gradle
    def lifecycle_version = "2.0.0"
    def room_version = "2.1.0-rc01"
    def anko_version = "0.10.4"
    def room_version = "2.1.0-rc01"
    def material_design_version = "1.0.0-rc01"
    def cardview_version = "1.0.0"


    // ViewModel and LiveData
    implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
    kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"

    //Room
    implementation "androidx.room:room-runtime:$room_version"
    kapt "androidx.room:room-compiler:$room_version"

    //Design
    implementation "com.google.android.material:material:$material_design_version"

    //CardView
    implementation "androidx.cardview:cardview:$cardview_version"

    //Anko
    implementation "org.jetbrains.anko:anko-commons:$anko_version"
```
This is better than before but still file can get cluttered when new dependencies gets added, there is still room for improvements.

###2. Better Approach
We can create a separate file for storing our dependencies and name it something like *deps.gradle*

```gradle
ext {
    def supportVersion = "27.1.1"
    supportDependencies = [
        design          : "com.android.support:design:$supportVersion",
        cardview        : "com.android.support:cardview-v7:$supportVersion",
        recyclerview    : "com.android.support:recyclerview-v7:$supportVersion"

   ]
}
```

And now import it into *build.gradle* like
```gradle
dependencies {
    implementation supportDependencies.design
    implementation supportDependencies.cardview
    implementation supportDependencies.recyclerview
}
```

or we can also do it in single line
```gradle
dependencies {
    implementation supportDependencies.value()
}
```

###3. Recommended Approach
This approach is best suited for Project with multiple modules.  

First we have to create a directory inside root project named **buildSrc** and add *Dependencies.kt* and *Version.kt*, these files will hold information about **all** dependencies in our project, Directory structure should look like this
```
<project>
├── buildSrc
│   └── src
│        └── main
│              └── java
│                    └── Dependencies.kt
│                    └── Version.kt
```
Now as the name suggest we will use *Dependencies.kt* to store all the dependencies and *Version.kt* to store versions.

Our lets look what our files will look like from inside.
```kotlin
//Version.kt will hold all the versions like this
object Version {
    // android configuration
    const val buildTools = "29.0.3"
    const val compileSdk = 29
    const val minSdk = 23
    const val targetSdk = 29
    const val versionCode = 1
    const val versionName = "1.0"

    //Libraries
    const val supportLib = "28.0.0"
    const val recyclerView = "1.0.0"
    const val androidx = "1.0.0"
    const val materialDesign = "1.0.0-rc01"
    const val mockito = "1.10.19"
    const val dagger2 = "2.21"
    const val room = "2.0.0-rc01"
}
```

```kotlin
//Dependencies.kt will hold all libraries like this
object Dependencies {
    
    //path to common dependencies (disscussed later)
    private const val path = "../commonFiles/gradleScript/"
    const val common = "${path}common.gradle"

    //path to local dependencies (disscussed later)
    const val dependency = "./gradleScript/dependencies.gradle"

    object Module {
        //Add your modules here
        const val data = ":data"
        const val cache = ":cache"
        const val remote = ":remote"
    }

    //Create object for every libraries being used to group all related dependencies together
    object Chucker {
        const val debug = "com.github.ChuckerTeam.Chucker:library:${Version.chucker}"
        const val release = "com.github.ChuckerTeam.Chucker:library-no-op:${Version.chucker}"
    }

    object Facebook {
        const val stetho = "com.facebook.stetho:stetho:${Version.stetho}"
        const val stethoNetwork = "com.facebook.stetho:stetho-okhttp3:${Version.stetho}"
    }

    object NavigationComponent {
        const val fragment = "androidx.navigation:navigation-fragment-ktx:${Version.navigation}"
        const val ui = "androidx.navigation:navigation-ui-ktx:${Version.navigation}"
    }
}
```
We are done with *Dependencies.kt* and *Version.kt*.

Now, Create a directory again under our root project to store our common dependencies, we will call it commonFiles, and create **common.gradle** inside gradleScript
```
<project>
├── commonFiles
│   └── gradleScript
│        └── common.gradle
```

Create a directory **gradleScript** in every module you are going to use, which will store all Dependencies related to that module add file dependencies.gradle in it.
```
<module>
│   └── gradleScript
│        └── dependencies.gradle
```

Let work with our common dependencies, it should look like this.
```kotlin
//common.gradle
import dependencies.Dependencies

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])

    implementation Dependencies.Kotlin.kotlin_stdlib_jdk7

    testImplementation Dependencies.Test.test_junit
    testImplementation Dependencies.Test.roboElectric
    testImplementation Dependencies.Test.android_test_room
    testImplementation Dependencies.Test.mockito
}
```
So we just simply access our dependencies from the Dependencies object we created before. Similarly in our local dependencies we can do

```kotlin
//dependencies.gradle
import dependencies.Dependencies

// applying common deps into local deps
apply from: Dependencies.common

dependencies {
    //App Module ( App Module ONLY TO BE INCLUDED IN MAIN APP MODULE)
    implementation project(Dependencies.Module.domain)
    implementation project(Dependencies.Module.cache)
    implementation project(Dependencies.Module.presentaion)

    //RxJava
    implementation Dependencies.RxJava.rxAndroid
    implementation Dependencies.RxJava.rxjava2
    implementation Dependencies.RxJava.rxBinding

    //Dagger2
    implementation Dependencies.Dagger.dagger2
    implementation Dependencies.Dagger.daggerAndroid
    implementation Dependencies.Dagger.daggerAndroidSupport
    kapt Dependencies.Dagger.processor
    kapt Dependencies.Dagger.compiler

}
```

Now the **Final Step** in every module level build.gradle we have to import the **dependencies.gradle**, it should look something like this

```kotlin
//build.gradle (local)
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: "kotlin-kapt"

//this will apply all the local deps stored in local dependencies.gradle
apply from: Dependencies.dependency

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.3"

    defaultConfig {
        applicationId "com.test.project"
        minSdkVersion Version.minSdk
        targetSdkVersion Version.targetSdk
        versionCode Version.versionCode
        versionName Version.versionName
    }
    ...
}
```

That is it we have made workflow that is easily salable for large project and is easily manageable

###Simple, clean and powerful isn’t it !!!

Just by applying small effort we have made our code more readable and manageable.

Article inspired by : https://github.com/happysingh23828/Android-Clean-Architecture

Stay Cool 😎.