image

Вступление


Статья пишется с прицелом на нативные андроид проекты, но, так как градл универсальная система сборки, в принципе, подойдет и для других проектов, которые может собрать градл. Идея не моя, я ее подчерпнул из гитхаб проекта у Jake Wharton SdkSearch — набор программ для поиска по документации андроид сдк.

Проблема


Современное приложение практически невозможно(нецелесообразно) написать не используя библиотеки. С появлением зависимостей возникает задача управления версионностью. Возможно для одномодульного андроид приложения средней руки это не составляет проблем, то в более крупных проектах с переиспользованием кода проблема актуальна. Особенно в управлении версиями support androidx библиотек, у которых версионность просто за гранью разумного, вы просто посмотрите AndroidX versions

Решение


При таком количестве зависимостей очень важно чтобы все модули зависели от одной версии библиотеки, иначе сюрпризы в рантайме будут вылазить в самом неожиданном месте.
Градл в качестве скриптового языка использует 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
}

Что получилось — исчезли версии библиотек у модулей, появилась иерархия библиотек
Также стоит отметить, что модуль не обязан зависеть от всех библиотек описанных в корневом скрипте, а только от тех, от которых необходимо.

Как я и упоминал выше, рабочий проект с использованием такой схемы находится тут.

Комментарии (6)


  1. vmm86
    26.09.2019 15:58

    Я столкнулся с досадным багом Gradle-проектов в IntelliJ IDEA — пришлось откатиться на Maven. После первого же обновления проекта из конфига после внесённых изменений слетает привязка папок (исходники и ресурсы, обычные и тестовые), даже если руками прописать их в конфиге. Есть возможность это исправить, или писать багрепорт в поддержку Jetbrains? lany, можете помочь?


    1. lany
      26.09.2019 16:17

      Лучше всего написать в саппорт. Я не специалист по гредлу.


  1. Firsto
    27.09.2019 08:13

    А Студия покажет, что есть новая версия библиотеки и через Alt+Enter вставит актуальную? :-)


    1. 200ton
      27.09.2019 08:15

      Да, покажет


  1. panman
    27.09.2019 11:45

    В данном подходе есть одна проблема. Android Studio перестает подсвечивать устаревшие версии библиотек. Если в ext выносить только версии, то подсветка останется.


    1. soul_survivor Автор
      27.09.2019 11:53

      Тут палка о двух концах — чтобы изменить версию библиотеки нужно пойти посмотреть, а не подняли они minSdk, не перешли ли на какую-то несовместимую зависимость, не сломали ли апи. Я обычно пересматриваю внешние зависимости где-то раз в пару месяцев либо когда узнал о новой версии из медиа — обычно стабильность билда важнее чем иметь самую самую последнюю версию библиотеки, даже если она вносит что-нибудь очень полезное.