Pull to refresh

Управление зависимостями в многомодульном проекте на градл

Reading time3 min
Views5.8K
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
}

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

Как я и упоминал выше, рабочий проект с использованием такой схемы находится тут.
Tags:
Hubs:
Total votes 12: ↑10 and ↓2+8
Comments8

Articles