تبدیل اسکریپت Gradle اندروید به کاتلین — از صفر تا صد
استفاده از زبانی واحد در سراسر یک پروژه میتواند موجب روانی کار و تسریع فرایند توسعه شود. با معرفی نسخه پایدار Kotlin DSL برای Gradle اینک همه چیز برای توسعه مدرن اندروید با استفاده از کاتلین مهیا شده است، زیرا کاتلین اینک زبان ترجیح داده شده برای توسعه اندروید محسوب میشود. در این مقاله به توضیح روش تبدیل اسکریپت Gradle اندروید به کاتلین میپردازیم. Kotlin DSL مزیتهای روشنی در مقابل Groovy DSL دارد که برخی از موارد آن به شرح زیر است:
- نوعبندی استاتیک و DSL با نوع امن.
- تابعهای مرتبه اول، متدهای بسط.
- تکمیل خودکار IDE، کمک به محتوا.
- ناوبری به منبع
- بازسازی کد
توجه کنید که قطعه کدهایی که در ادامه این مقاله آمدهاند، صرفاً به منظور روشن ساختن تغییرات مهم در چارچوب مفروض هستند و نماینده فایلهای کامل به حساب نمیآیند.
در بخشهای بعدی با روش بهکارگیری Kotlin DSL بیشتر آشنا میشویم.
اطمینان یابید از آخرین نسخه Gradle Wrapper استفاده میکنید
برای این که بتوانید از Kotlin DSL و جدیدترین قابلیتهای آن بهره بگیرید، باید مطمئن شوید که از جدیدترین نسخه آن استفاده میکنید. در پروژههای اندروید، توصیه شده که از Gradle 5.0 به بالا و از Android Gradle Plugin 3.4 استفاده کنید.
به دایرکتوری ریشه پروژه بروید، ترمینال را باز کنید و با اجرای دستور زیر مطمئن شوید که از جدیدترین نسخه Gradle wrapper استفاده میکنید:
gradle wrapper --gradle-version X.Y.Z --distribution-type all
در کد فوق به جای X.Y.Z جدیدترین نسخه (+) gradle را قرار دهید. این کار نیازمند نصب نسخهای از Gradle (+) روی سیستم است شما باید همه انواع توزیع را دریافت کنید، زیرا به قابلیت تکمیل خودکار IDE کمک میکند و امکان ناوبری به کد منبع Gradle را میدهد. پس از اجرای این دستور، فایل شما باید برای ارجاع به آخرین توزیع Gradle بهروزرسانی شود.
اگر از قبل یک Wrapper پیکربندی کردهاید و تنها میخواهید نسخه آن را مستقلاً بررسی کنید به فایل زیر سر بزنید:
./gradle/wrapper/gradle-wrapper.properties
فایل gradle-wrapper.properties باید به صورت زیر باشد:
1distributionBase=GRADLE_USER_HOME
2distributionPath=wrapper/dists
3distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip
4zipStoreBase=GRADLE_USER_HOME
5zipStorePath=wrapper/dists
زمانی که از شما خواسته میشود، پروژه خود را مجدداً همگامسازی کنید و یک بیلد دیباگ بسازید تا مطمئن شوید که همه چیز به درستی کار میکند:
./gradlew assembleDebug
نکته: پیشنهاد میشود هر بار که تغییراتی در رابطه با migration ایجاد میکنید، یک بیلد دیباگ بسازید زیرا IDE در صورتی که یک همگامسازی موفق نداشته باشید، چندان مفید نخواهد بود. برای شناسایی خطاها باید به کنسول نگاه کنید.
آمادهسازی Groovy DSL موجود
با آمادهسازی Groovy DSL موجود از بروز خطاهای زیاد ساختاری در زمان تبدیل به Kotlin DSL جلوگیری میکنید. از آنجا که Groovy DSL و Kotlin DSL مشابهتهای زیادی دارند کافی است اسکریپتهای موجود را تطبیق کنیم و نیازی به بازنویسی آنها نیست.
پیش از آن که بخواهیم تغییراتی بدهیم، ابتدا باید Gradle auto-sync را غیرفعال کنیم. در غیر این صورت Gradle هر بار که فایلی تغییر پیدا کند، اقدام به همگامسازی مجدد میکند.
ما اسکریپتهای بیلد خود را با استفاده از دو مورد زیر آمادهسازی میکنیم و همچنان یک Groovy DSL معتبر داریم:
- ‘ را با “ عوض میکنیم.
- فاصلههای خالی را با یکی از موارد =، یعنی انتساب و یا ()، یعنی فراخوانی تابع جایگزین میکنیم.
مورد دوم فوق بیشتر جالب توجه است. Groovy DSL به طور پیشفرض بین انتسابها و فراخوانیهای تابع تفاوتی قائل نمیشود. ما ترجیح میدهیم از ساختار منسجمتر استفاده کنیم تا این که کد تمیزتری داشته باشیم، از این رو الزام این موارد در خصوص Kotlin DSL مناسب است.
فایلهای زیر باید بهروزرسانی شوند:
- settings.gradle
- build.gradle (Project)
- build.gradle (Module)
جایگزینی ‘ با “
سرراستترین روش برای این جایگزینی و بازنویسی فایلها استفاده از گزینه Replace All است.
بدین منظور در ویندوز و لینوکس کلیدهای Ctrl+R و در سیستمهای مک کلیدهای CMD+R را بزنید.
زمانی که از شما خواسته شد، پروژه را «همگامسازی مجدد» (Re-sync) بکنید. سپس با دستور زیر یک بیلد دیباگ بسازید تا مطمئن شوید که همه چیز به درستی کار میکند:
./gradlew assembleDebug
جایگزینی فواصل خالی با انتساب صریح و فراخوانیهای تابع
مورد دیگری که باید بررسی کنیم این است که آیا فواصل خالی نمایشدهنده فراخوانیهای تابع هستند یا انتساب. سادهترین روش متد آزمونوخطا در سراسر فایل است. بدین ترتیب IDE فرضیههای نادرست را هایلایت میکند و به شما نشان میدهد که اشتباه کردهاید.
برای نمونه زمانی که این کار را انجام دادید، فایل build.gradle (Module) میتواند به صورت زیر درآید:
1android {
2 compileSdkVersion = 29
3 buildToolsVersion = "29.0.2"
4 defaultConfig {
5 applicationId = "com.andreramon.example"
6 minSdkVersion(19)
7 targetSdkVersion(29)
8 versionCode = 1
9 versionName = "1.0"
10 testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
11 }
12 buildTypes {
13 release {
14 minifyEnabled = false
15 proguardFiles(getDefaultProguardFile(
16 "proguard-android-optimize.txt"),
17 "proguard-rules.pro")
18 }
19 }
20}
21
22dependencies {
23 implementation(fileTree(dir: "libs", include: ["*.jar"]))
24 implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.3.50")
25 implementation("androidx.appcompat:appcompat:1.1.0")
26 // ...
27}
این فایل همچنان دارای ساختار Groovy است، زیرا هنوز نام فایلها را به صورت .gradle.kts تغییر ندادهایم.
تغییر نام فایلهای gradle. به gradle.kts.
زمانی که نام فایلها را تغییر میدهیم، باید از ساختار جدید Kotlin DSL استفاده کنیم. پسوند .kts به Gradle نشان میدهد که فایل مورد استفاده به صورت Kotlin DSL است.
تغییر نام settings.gradle به settings.gradle.kts
این کوچکترین بخش است و از این رو از همینجا آغاز میکنیم. زمانی که آخرین گام را طی کنید، این فایل باید به صورت خودکار عمل کند. محل این فایل در مسیر ./settings.gradle است.
نکته: ممکن است IDE به شما اعلام کند که نمیتوانید نام فایل را تغییر دهید. در این حالت، کافی است روی Continue و Refactor کلیک کنید.
تغییر نام build.gradle (Project) به build.gradle.kts
در این بخش نام فایل بیلد در سطح پروژه را تغییر میدهیم. در این فایل احتمالاً برخی از متغیرهای سراسری مانند آن چه در ادامه آمده است قرار دارد.
محل فایل: build.gradle/.
بهروزرسانی متغیرها
اعلانهای متغیر مانند زیر برای اشتراک متغیرها بین فایلهای اسکریپت مختلف دیگر ممکن نیستند. فعلاً مداخلی مانند زیر را صرفاً حذف میکنیم:
ext.kotlin_version = “1.3.50”
فراموش نکنید که قالبهای رشتهای را با مقادیر واقعی عوض کنید. متغیر ممکن است در فایلهای دیگر نیز مورد دسترسی قرار گرفته باشد و از این رو بهتر است به همه اسکریپتهای بیلد نگاه کنید تا مطمئن شوید.
بهروزرسانی وظایف
در این مثال، وظیفه clean باید بهروزرسانی شود. از آنجا که قبلاً نام فایل را به .kts عوض کردهایم، وظایف دیگر ساختار معتبری ندارند و باید بر همین اساس تغییر یابند. بنابراین کد زیر:
1 task clean(type: Delete) {
2 delete rootProject.buildDir
3 }
با کد زیر تعویض میشود:
1 tasks.register("clean", Delete::class) {
2 delete(rootProject.buildDir)
3 }
نکته: میتوانید برخی از نمونههایی را که Gradle ارائه میکند (+) مورد بررسی قرار دهید.
تغییر دادن نام build.gradle (Module) به build.gradle.kts
این فایل نماینده پیکربندی بلد در سطح ماژول است و برای نمونه در آن وابستگیهای اپلیکیشن تعریف میشوند.
محل فایل: app/build.gradle/.
بهروزرسانی پلاگینها
احتمالاً در ابتدای این فایل یک پلاگین Gradle اعلان کردهاید. برای نمونه میتواند پلاگین kotlin-android باشد، از آنجا که DSL-ها باید کمترین افزونگی را داشته باشند، باید آنها را درون بلوک plugins قرار دهیم.
با استفاده از Kotlin DSL، دیگر خبری از فراخوانیهای مکرر apply plugin نخواهد بود. نکته دیگری که در اینجا شایان توجه است این است که Kotlin DSL میتواند تابع بسطی به نام kotlin در اختیار ما قرار دهد تا پیشوند kotlin. را حذف کنیم.
بنابراین به جای کد زیر:
1 apply plugin: "com.android.application"
2 apply plugin: "kotlin-android"
3 apply plugin: "kotlin-android-extensions"
میتوانیم از کد زیر استفاده کنیم:
1plugins {
2 id("com.android.application")
3 kotlin("android")
4 kotlin("android.extensions")
5}
بهروزرسانی انواع بیلد
چنان که در مثال کد زیر میبینید، راهحل Kotlin در این مورد به اندازه کافی سرراست نیست و نویز زیادی وارد کد میکند.
1android {
2 buildTypes {
3 release {
4 minifyEnabled = false
5 proguardFiles(getDefaultProguardFile(
6 "proguard-android-optimize.txt"),
7 "proguard-rules.pro")
8 }
9 }
10}
در بلوک buildTypes باید دو تغییر ایجاد کنیم:
- Release یک رشته است که به یک تابع ارسال میشود و این تابع از سوی Groovy همانند قبل فراخوانی میشود. ما باید در زمان استفاده از Kotlin DSL به روش منسجمتری عمل کنیم و به صراحت آن را به عنوان یک فراخوانی تابع اعلان نماییم.
- minifyEnabled نام واقعی مشخص نیست و نام آن isMinifyEnabled است.
بنابراین کد به صورت زیر درمیآید:
1android {
2 buildTypes {
3 getByName("release") {
4 isMinifyEnabled = false
5 proguardFiles(getDefaultProguardFile(
6 "proguard-android-optimize.txt"),
7 "proguard-rules.pro")
8 }
9 }
10}
بهروزرسانی پیادهسازیها
برای این که کارها عملیاتی شود باید روش پیادهسازی برخی موارد مانند پیادهسازی fileTree در این مورد را عوض کنیم. از آنجا که همه انواع توزیع را قبلاً ایمپورت کردهایم، میتوانیم با رفتن به بخش تعاریف، به بررسی کد منبع بپردازیم. امضای متد fileTree به صورت زیر است:
fileTree(Map<String,?> args)
چنان که میبینید، این متد یک map از آرگومانها قبول میکند. در کاتلین این را از طریق جفتها بیان میکنیم که با استفاده از تابع درونخطی to شناسایی میشود. بنابراین به جای کد زیر:
1implementation(fileTree(dir: 'libs', include: ['*.jar']))
از این کد استفاده میکنیم:
1implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
مدیریت وابستگیها به روش کاتلین
زمانی که Gradle را اجرا میکنید، به طور خودکار کد منبع پوشه buildSrc را کامپایل کرده و آن را به classpath اسکریپتهای بیلد اضافه میکند. بدین ترتیب میتوانید وابستگیها و نسخههای پروژه خود را به روشی تمیز با استفاده از اشیای سینگلتون همراه با ثابتها و تعاریف نسخه خصوصی سازماندهی کنید.
مدیریت نسخهها و وابستگیها در یک منبع واحد برای جلوگیری از رفتارهای غیرمنتظره حائز اهمیت است. به علاوه ترجیح میدهیم که نمادگذاری نسخهها و وابستگی را در همان شیء با پیروی از «اصل وفاداری» (principle of locality.) حفظ کنیم. پوشه buildSrc را در پوشه ریشه پروژه ایجاد کنید. درون این پوشه باید دو چیز را داشته باشید که یکی فایلی به نام build.gradle.kts و دیگری یک ساختار maven است.
ابتدا یک فایل به نام build.gradle.kts با محتوای زیر ایجاد کنید:
1plugins {
2 `kotlin-dsl`
3}
4
5repositories {
6 jcenter()
7}
یک ساختار دایرکتوری maven به صورت زیر ایجاد کنید:
src/main/java/your/package/
در این مثال ساختار دایرکتوری به صورت زیر است:
src/main/java/com/andreramon/example/
تعریف کردن وابستگیها
در انتهای ساختار دایرکتوری جدیداً ایجاد شده، دو فایل به نامهای Libs.kt و Versions.kt بسازید. درون فایل Libs.kt چند سینگلتون با استفاده از اعلان شیء ایجاد کنید:
1object Libs {
2 const val androidGradlePlugin = "com.android.tools.build:gradle:3.5.0"
3
4 object AndroidX {
5 const val appCompat = "androidx.appcompat:appcompat:1.1.0"
6 // ...
7
8 object Lifecycle {
9 private const val version = "2.1.0"
10 const val extensions = "androidx.lifecycle:lifecycle-extensions:$version"
11 // ...
12 }
13
14 // ...
15
16 object Test {
17 const val espressoCore = "androidx.test.espresso:espresso-core:3.2.0"
18 }
19 }
20
21 object Kotlin {
22 private const val version = "1.3.50"
23 const val stdlib = "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$version"
24 // ...
25 }
26
27 // ...
28
29 object Test {
30 const val junit = "junit:junit:4.12"
31 }
32}
تعریف نسخهها
اگر میخواهید همین فرایند را در مورد فایل Versions.kt تکرار کنید. ما نسخه بندی مرتبط با بیلد را در اینجا به صورت نسخههای compileSdk ،targetSdk و minSdk حفظ کردهایم.
دسترسی به وابستگیها
هم اینک میتوانید به این مقادیر به روش نرمال شیءگرا دسترسی یافته و از تکمیل خودکار IDE برای دسترسی به آنها استفاده کنید.
1android {
2 compileSdkVersion(Versions.Build.compileSdk)
3 // ...
4
5 defaultConfig {
6 minSdkVersion(Versions.Build.minSdk)
7 targetSdkVersion(Versions.Build.targetSdk)
8 // ...
9 }
10}
11
12dependencies {
13 implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.jar"))))
14
15 implementation(Libs.AndroidX.appCompat)
16 implementation(Libs.AndroidX.constraintLayout)
17 implementation(Libs.AndroidX.coreKtx)
18
19 kapt(Libs.AndroidX.Lifecycle.compiler)
20 implementation(Libs.AndroidX.Lifecycle.extensions)
21 implementation(Libs.AndroidX.Lifecycle.viewModel)
22
23 implementation(Libs.Kotlin.stdlib)
24 // ...
25}
نکته: اگر پس از این تغییر با خطای Unresolved reference مواجه شدید، فایلها را به /src/main/java انتقال داده و تعریف پکیج درون Libs.kt و Versions.kt را پاک کنید. سپس ایمپورتهای متناظر درون اسکریپتهای Gradle را نیز پاک کرده و یک همگامسازی بکنید.
پیکربندیهای امضا
یکی از مشکلاتی که در زمان مهاجرت پروژهها به Kotlin DSL رخ میدهد، پیکربندی امضا در فایل build.gradle.kts (Module) است. به طور معمول نیاز به ایجاد پیکربندی امضای دیباگ وجود ندارد، زیرا از قبل تعریف شده است. با نگاه کردن به پیکربندی امضای release با وضعیت متفاوتی مواجه میشویم. به نظر میرسد که Groovy پیکربندی امضای release را به صورت صریح ایجاد میکند، در حالی که باید create را در زمان استفاده از کاتلین به صورت صریح فراخوانی کنید.
بنابراین بخش پیکربندی امضا در فایل build.gradle.kts از وضعیت زیر:
1signingConfigs {
2 debug {
3 keyAlias = keystoreProperties["debugKeyAlias"]
4 // ...
5 }
6 release {
7 keyAlias = keystoreProperties["debugKeyAlias"]
8 // ...
9 }
10}
11buildTypes {
12 debug {
13 signingConfig signingConfigs.debug
14 // ...
15 }
16 release {
17 signingConfig signingConfigs.debug
18 // ...
19 }
20}
به وضعیت زیر تغییر مییابد:
1signingConfigs {
2 getByName(“debug”) {
3 keyAlias = keystoreProperties["debugKeyAlias"].toString()
4 // ...
5 }
6 create(“release”) {
7 keyAlias = keystoreProperties["releaseKeyAlias"].toString()
8 // ...
9 }
10}
11buildTypes {
12 getByName("debug") {
13 signingConfig = signingConfigs.getByName("debug")
14 // ...
15 }
16 getByName("release") {
17 signingConfig = signingConfigs.getByName("release")
18 // ...
19 }
20}
سخن پایانی
به این ترتیب شما موفق شدهاید با موفقیت از Kotlin DSL استفاده کنید. برای مطمئن شدن از این که همه چیز به درستی کار میکند، دستور زیر را اجرا کنید:
./gradlew assembleDebug
اسکریپتهای بیلد شما اینک باید همان هایلایت سینتکس کد معمول کاتلین را داشته باشند.
اگر این مطلب برای شما مفید بوده است، آموزشهای زیر نیز به شما پیشنهاد میشوند:
- مجموعه آموزشهای برنامهنویسی اندروید
- مجموعه آموزشهای برنامهنویسی
- آموزش مقدماتی زبان برنامه نویسی کاتلین (Kotlin) برای توسعه اندروید (Android)
- برنامه نویسی اندروید با کاتلین — راهنمای شروع به کار
- زبان برنامه نویسی کاتلین (Kotlin) — راهنمای کاربردی
==