تفکیک وابستگی Gradle

فایل‌های ساخت شما وابستگی‌های مستقیم شما را مشخص می‌کنند، اما هر یک از این وابستگی‌ها می‌تواند به موارد دیگری نیاز داشته باشد. این وابستگی های گذرا به سرعت نمودار وابستگی کلی شما را، اغلب با نسخه های متناقض، رشد می دهند.

وقتی بخش‌های minor (ویژگی‌های جدید) یا patch (رفع اشکال) تغییر می‌کنند، کتابخانه همچنان به احتمال زیاد سازگار است و کمتر بر برنامه شما تأثیر می‌گذارد.

به عنوان مثال، فرض کنید برنامه شما به کتابخانه A و کتابخانه B بستگی دارد که به نوبه خود به نسخه های مختلف کتابخانه C بستگی دارد.

برنامه شما به کتابخانه A و کتابخانه B بستگی دارد که به نوبه خود به نسخه های مختلف کتابخانه C بستگی دارد. Gradle جدیدترین نسخه کتابخانه C را انتخاب می کند.
شکل 1. تضاد نسخه گذرا. Gradle به جدیدترین نسخه (به طور پیش فرض) حل و فصل می شود.

در این حالت، Gradle جدیدترین نسخه کتابخانه C را به طور پیش فرض انتخاب می کند که ممکن است باعث ایجاد مشکلاتی در کامپایل یا زمان اجرا شود. در این مثال، کتابخانه C به 2.1.1 حل شده است، اما توجه داشته باشید که کتابخانه A کتابخانه C 1.0.3 را درخواست کرده است. بخش عمده ای از شماره نسخه تغییر کرده است، که نشان دهنده تغییرات ناسازگار، مانند توابع یا انواع حذف شده است. این می‌تواند باعث از کار افتادن تماس‌های برقرار شده از کتابخانه A شود.

برنامه شما می‌تواند وابستگی‌های مستقیم داشته باشد که وابستگی‌های گذرا نیز هستند.

برنامه شما به کتابخانه A و کتابخانه C بستگی دارد. کتابخانه A به نسخه جدیدتری از کتابخانه C بستگی دارد. Gradle جدیدترین نسخه کتابخانه C را انتخاب می کند.
شکل 2. یکی دیگر از تضادهای نسخه گذرا. در اینجا، Gradle به نسخه انتقالی حل می شود و برنامه شما آن نسخه جدیدتر را می بیند.

در چنین موردی، وابستگی‌های انتقالی جدیدتر می‌توانند نسخه‌ای را که مستقیماً در برنامه خود درخواست می‌کنید لغو کنند.

Gradle به تمام نسخه‌های کاندید برای همه وابستگی‌ها در نمودار نگاه می‌کند تا جدیدترین نسخه هر وابستگی را تعیین کند. می‌توانید از وظایف پایه Gradle و ابزارهای پیشرفته‌تر برای تعیین نسخه‌های هر وابستگی Gradle استفاده کنید. مقایسه تغییرات در این قطعنامه برای درک و کاهش خطرات ارتقاء شما کلیدی است.

به عنوان مثال، می‌توانید با اجرای ./gradlew app:dependencies از تکلیف Gradle dependencies استفاده کنید تا درختی از تمام وابستگی‌های مورد استفاده توسط ماژول برنامه شما نمایش داده شود. اجرای این برنامه در برابر برنامه ای که از کتابخانه ها همانطور که در شکل 2 نشان داده شده است، استفاده می کند

1: releaseRuntimeClasspath - Runtime classpath of /release.
2: +--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0
3: |    +--- ... (omitted for brevity) ...
4: +--- com.sample:library.a:1.2.3
5: |    +--- com.sample:library.c:2.1.1
6: |    |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
7: |    \--- org.jetbrains.kotlin:kotlin-stdlib:2.0.0 (*)
8: +--- com.sample:library.c:1.4.1 -> 2.1.1 (*)

این بخش از گزارش برخی از وابستگی‌های حل‌شده برای پیکربندی releaseRuntimeClasspath را نشان می‌دهد.

هر زمان که -> در گزارش وابستگی های خود مشاهده کردید، یک درخواست کننده (برنامه شما یا کتابخانه دیگری) از نسخه ای از آن وابستگی استفاده می کند که انتظارش را ندارد. در بسیاری از موارد، این مشکلی ایجاد نمی کند، زیرا اکثر کتابخانه ها برای سازگاری با عقب نوشته شده اند. با این حال، برخی از کتابخانه‌ها ممکن است تغییرات ناسازگاری ایجاد کنند و این گزارش می‌تواند به شما کمک کند تا مشخص کنید مشکلات جدید در رفتار برنامه‌تان از کجا می‌آیند.

جزئیات بیشتر در مورد استفاده از گزارش وابستگی Gradle را می‌توانید در View and Dependencies Debug پیدا کنید.

می‌توانید نسخه‌های درخواستی را مستقیماً، در کاتالوگ نسخه یا در صورت‌حساب مواد (BOM) مشخص کنید.

وضوح مشخصات نسخه مستقیم

نسخه‌هایی از وابستگی‌هایی که شما مشخص می‌کنید، کاندیدای وضوح نسخه می‌شوند.

به عنوان مثال، برای درخواست نسخه 1.7.3 کتابخانه androidx.compose.ui:ui به عنوان یک وابستگی در app/build.gradle.kts :

dependencies {
    implementation("androidx.compose.ui:ui:1.7.3")
}

نسخه 1.7.3 به نسخه کاندید تبدیل می شود. Gradle به آخرین نسخه از بین 1.7.3 و سایر نسخه های همان کتابخانه که توسط وابستگی های متعدی درخواست شده است، حل می شود.

وضوح کاتالوگ نسخه

کاتالوگ های نسخه متغیرهایی را برای ردیابی نسخه وابستگی های مورد استفاده در برنامه شما تعریف می کنند. اگر از متغیری از کاتالوگ نسخه استفاده می کنید، وابستگی های مشخص شده آن متغیر به نامزدهای وضوح نسخه اضافه می شود. متغیرهای استفاده نشده در کاتالوگ نسخه نادیده گرفته می شوند.

به عنوان مثال، برای تعیین نسخه 1.7.3 androidx.compose.ui:ui به عنوان یک وابستگی در فایل gradle/libs.versions.toml خود:

[versions]
ui = "1.7.3"

[libraries]
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }

این یک متغیر به نام libs.androidx.compose.ui برای نمایش کتابخانه تعریف می کند. این نسخه یک نامزد در نظر گرفته نمی شود مگر اینکه از آن متغیر برای تعیین یک وابستگی استفاده کنید.

برای درخواست کتابخانه و نسخه آن در app/build.gradle.kts :

dependencies {
    implementation(libs.androidx.compose.ui)
}

Gradle به همان روشی که برای مشخصات مستقیم حل کرد.

قطعنامه بیل مواد (BOM).

نسخه‌های همه کتابخانه‌هایی که در BOM ظاهر می‌شوند، کاندیدای وضوح نسخه می‌شوند. توجه داشته باشید که کتابخانه ها تنها در صورتی به عنوان وابستگی استفاده می شوند که به صورت مستقیم یا غیر مستقیم مشخص شده باشند. کتابخانه های دیگر در BOM نادیده گرفته می شوند.

نسخه های BOM بر وابستگی های مستقیم شما و همچنین تمام وابستگی های گذرا که در BOM ظاهر می شوند تأثیر می گذارد.

برای مثال، در app/build.gradle.kts خود یک BOM را به عنوان وابستگی پلتفرم مشخص کنید:

dependencies {
    implementation(platform("androidx.compose:compose-bom:2024.10.00"))
    implementation("androidx.compose.ui:ui")
}

هر کتابخانه ای که می خواهید به عنوان وابستگی استفاده کنید نیازی به مشخصات نسخه ندارد. نسخه درخواستی از BOM می آید.

توجه داشته باشید که می توانید از کاتالوگ نسخه برای ایجاد متغیرها برای BOM و کتابخانه ها نیز استفاده کنید. شماره‌های نسخه را در فهرست نسخه برای کتابخانه‌هایی که در وابستگی BOM ظاهر می‌شوند حذف کنید.

به عنوان مثال، کاتالوگ نسخه شما حاوی BOM و شماره نسخه آن است، اما نسخه ای را برای کتابخانه هایی که از BOM به آنها ارجاع می دهید، مشخص نمی کند:

[versions]
composeBom = "2024.10.00"

[libraries]
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }

app/build.gradle.kts شما به BOM و کتابخانه ها با استفاده از متغیرهای تعریف شده در کاتالوگ نسخه ارجاع می دهد:

dependencies {
    implementation(platform(libs.androidx.compose.bom))
    implementation(libs.androidx.compose.ui)
}

نسخه ای از آن کتابخانه مشخص شده در BOM کاندیدای رزولوشن Gradle می شود. علاوه بر این، تمام نسخه‌های کتابخانه دیگر مشخص‌شده در BOM به نسخه‌های کاندید تبدیل می‌شوند، چه مستقیماً از آنها به عنوان وابستگی استفاده کنید یا نه.

برای مثال، فرض کنید یک BOM نسخه‌هایی را برای کتابخانه‌های A، B و C مشخص می‌کند. برنامه شما می‌خواهد مستقیماً از کتابخانه A به عنوان یک وابستگی و همچنین کتابخانه D استفاده کند. کتابخانه D از کتابخانه B به عنوان یک وابستگی استفاده می‌کند. هیچ چیز از کتابخانه C استفاده نمی کند.

یک BOM شامل نسخه هایی برای کتابخانه های A، B و C است. برنامه شما از کتابخانه های A و D به عنوان وابستگی استفاده می کند. کتابخانه D از کتابخانه B به عنوان یک وابستگی استفاده می کند. کتابخانه C به طور مستقیم یا غیر مستقیم در این برنامه استفاده نمی شود.
شکل 3. سناریوی BOM.

کتابخانه های A، B و D وابستگی هایی در برنامه هستند. کتابخانه C نادیده گرفته می شود. Gradle از نسخه های A و B مشخص شده در BOM به عنوان کاندید استفاده می کند، حتی اگر شما کتابخانه B را مستقیماً به عنوان یک وابستگی مشخص نکنید.

اگر کتابخانه D نسخه ای از کتابخانه B را کمتر از 2.0.1 درخواست کند، Gradle به 2.0.1 حل می شود. اگر کتابخانه D نسخه بالاتری از کتابخانه B را درخواست کند، Gradle آن نسخه را حل می کند.