Вступление
Статья пишется с прицелом на нативные андроид проекты, но, так как градл универсальная система сборки, в принципе, подойдет и для других проектов, которые может собрать градл. Идея не моя, я ее подчерпнул из гитхаб проекта у Jake Wharton SdkSearch — набор программ для поиска по документации андроид сдк.
Проблема
Современное приложение практически невозможно(нецелесообразно) написать не используя библиотеки. С появлением зависимостей возникает задача управления версионностью. Возможно для одномодульного андроид приложения средней руки это не составляет проблем, то в более крупных проектах с переиспользованием кода проблема актуальна. Особенно в управлении версиями
Решение
При таком количестве зависимостей очень важно чтобы все модули зависели от одной версии библиотеки, иначе сюрпризы в рантайме будут вылазить в самом неожиданном месте.
Градл в качестве скриптового языка использует groovy, что в паре с динамической типизацией дает удобную возможность упорядочить управление зависимостями.
В градл DSL есть возможность добавлять свойства в проект при помощи объекта ext ExtraPropertiesExtension. Также, внутри скрипта модуля можно обратиться к скрипту проекта(корневого модуля), что позволяет задекларировать все версии зависимостей в корневом скрипте, а ссылаться на них уже можно из любого модуля внутри.
Для примера, пусть наше андроид приложение использует зависимости: kotlin, kotlin stdlib, kotlin junit, facebook sdk, androidx, google material
Корневой build.gradle:
buildscript {
ext {
versions = [
'kotlin': '1.3.50',
'fb': '4.40.0'
]
deps = [
'kotlin': [
'stdlib': [
'jdk': "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${versions.kotlin}"
],
'test': [
'common': "org.jetbrains.kotlin:kotlin-test-common:${versions.kotlin}",
'annotations': "org.jetbrains.kotlin:kotlin-test-annotations-common:${versions.kotlin}",
'jdk': "org.jetbrains.kotlin:kotlin-test-junit:${versions.kotlin}"
]
],
'androidx' : [
'annotation': "androidx.annotation:annotation:1.1.0",
'appCompat': 'androidx.appcompat:appcompat:1.1.0',
'constraintLayout': 'androidx.constraintlayout:constraintlayout:1.1.3',
'ktx': 'androidx.core:core-ktx:1.1.0',
'dynamicAnimation': 'androidx.dynamicanimation:dynamicanimation:1.0.0',
'gridLayout': 'androidx.gridlayout:gridlayout:1.0.0',
'localBroadcastManager': 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0',
'multidex': 'androidx.multidex:multidex:2.0.1',
'recyclerView': 'androidx.recyclerview:recyclerview:1.1.0-beta04'
],
'material': 'com.google.android.material:material:1.0.0',
'fb': [
'core': "com.facebook.android:facebook-core:${versions.fb}",
'login': "com.facebook.android:facebook-login:${versions.fb}",
'share': "com.facebook.android:facebook-share:${versions.fb}"
]
]
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.6.0-alpha11'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:${versions.kotlin}"
}
}
Пара моментов:
Если несколько зависимостей имеют одну версионность, например части одного большого сдк, то версия добавляется в свойство versions, как например с версиями котлина и фейсбука. Если же зависимость в виде одной строки, как например google material, то выносить версию нецелесообразно. Версии библиотек androidx тоже не нужно выносить, т.к. гугл отказался от выравнивания версий между собой для ускорения релизов отдельных библиотек.
В результате такого определения, во всех дочерних модулях декларация зависимостей становится лаконичной иерархией избавленной от версионности, т.к. теперь все модули зависят от одних и тех же версий библиотек.
пример раздела зависимостей модуля в build.gradle
dependencies {
implementation deps.kotlin.stdlib.jdk
implementation deps.androidx.appCompat
implementation deps.androidx.browser
implementation deps.androidx.cardView
implementation deps.androidx.constraintLayout
implementation deps.androidx.ktx
implementation deps.androidx.multidex
implementation deps.androidx.recyclerView
implementation deps.material
implementation deps.fb.core
implementation deps.fb.login
implementation deps.fb.share
testImplementation deps.kotlin.test.jdk
}
Что получилось — исчезли версии библиотек у модулей, появилась иерархия библиотек
Также стоит отметить, что модуль не обязан зависеть от всех библиотек описанных в корневом скрипте, а только от тех, от которых необходимо.
Как я и упоминал выше, рабочий проект с использованием такой схемы находится тут.