برنامههای اندروید معمولاً با استفاده از سیستم ساخت Gradle ساخته میشوند. قبل از اینکه به جزئیات نحوه پیکربندی ساخت بپردازیم، مفاهیم پشت ساخت را بررسی خواهیم کرد تا بتوانید سیستم را به طور کلی ببینید.
ساخت چیست؟
یک سیستم ساخت، کد منبع شما را به یک برنامه اجرایی تبدیل میکند. ساختها اغلب شامل ابزارهای متعددی برای تجزیه و تحلیل، کامپایل، پیوند و بستهبندی برنامه یا کتابخانه شما هستند. Gradle از یک رویکرد مبتنی بر وظیفه برای سازماندهی و اجرای این دستورات استفاده میکند.
وظایف، دستوراتی را که ورودیهایشان را به خروجی تبدیل میکنند، کپسولهسازی میکنند. افزونهها، وظایف و پیکربندی آنها را تعریف میکنند. اعمال یک افزونه به ساخت شما، وظایف آن را ثبت میکند و آنها را با استفاده از ورودیها و خروجیهایشان به هم متصل میکند. به عنوان مثال، اعمال افزونه Android Gradle (AGP) به فایل ساخت شما، تمام وظایف لازم برای ساخت یک APK یا یک کتابخانه اندروید را ثبت میکند. افزونه java-library به شما امکان میدهد یک jar از کد منبع جاوا بسازید. افزونههای مشابهی برای Kotlin و سایر زبانها وجود دارد، اما افزونههای دیگر برای گسترش افزونهها در نظر گرفته شدهاند. به عنوان مثال، افزونه protobuf برای افزودن پشتیبانی protobuf به افزونههای موجود مانند AGP یا java-library در نظر گرفته شده است.
Gradle قرارداد را به پیکربندی ترجیح میدهد، بنابراین افزونهها با مقادیر پیشفرض خوبی از قبل ارائه میشوند، اما میتوانید ساخت را از طریق یک زبان خاص دامنه (DSL) اعلانی پیکربندی کنید. DSL طوری طراحی شده است که میتوانید مشخص کنید چه چیزی ساخته شود، نه اینکه چگونه ساخته شود. منطق موجود در افزونهها "نحوه" را مدیریت میکند. این پیکربندی در چندین فایل ساخت در پروژه (و زیرپروژهها) شما مشخص شده است.
ورودیهای وظیفه میتوانند فایلها و دایرکتوریها و همچنین سایر اطلاعات کدگذاری شده به صورت انواع جاوا (عدد صحیح، رشتهها یا کلاسهای سفارشی) باشند. خروجیها فقط میتوانند دایرکتوری یا فایل باشند زیرا باید روی دیسک نوشته شوند. اتصال خروجی یک وظیفه به ورودی وظیفه دیگر، وظایف را به هم پیوند میدهد به طوری که یکی باید قبل از دیگری اجرا شود.
اگرچه Gradle از نوشتن کد دلخواه و تعریف وظایف در فایلهای ساخت شما پشتیبانی میکند، اما این میتواند درک ساخت شما را برای ابزارها و نگهداری آن را برای شما دشوارتر کند. به عنوان مثال، میتوانید تستهایی برای کد درون افزونهها بنویسید اما نه در فایلهای ساخت. در عوض، باید منطق ساخت و تعریف وظایف را به افزونهها (که شما یا شخص دیگری تعریف میکنید) محدود کنید و نحوه استفاده از آن منطق را در فایلهای ساخت خود اعلام کنید.
وقتی یک Gradle build اجرا میشود چه اتفاقی میافتد؟
ساختهای Gradle در سه مرحله اجرا میشوند. هر یک از این مراحل، بخشهای مختلفی از کدی را که در فایلهای ساخت خود تعریف میکنید، اجرا میکند.
- مقداردهی اولیه تعیین میکند که کدام پروژهها و زیرپروژهها در ساخت گنجانده شدهاند و مسیرهای کلاس حاوی فایلهای ساخت و افزونههای اعمالشده را تنظیم میکند. این مرحله بر روی یک فایل تنظیمات تمرکز دارد که در آن پروژههایی را برای ساخت اعلام میکنید و مکانهایی را که افزونهها و کتابخانهها از آنجا دریافت میشوند.
- پیکربندی، وظایف مربوط به هر پروژه را ثبت میکند و فایل ساخت را برای اعمال مشخصات ساخت کاربر اجرا میکند. درک این نکته مهم است که کد پیکربندی شما به دادهها یا فایلهای تولید شده در طول اجرا دسترسی نخواهد داشت.
- اجرا، «ساخت» واقعی برنامه شما را انجام میدهد. خروجی پیکربندی، یک نمودار جهتدار غیرمدور (DAG) از وظایف است که تمام مراحل ساخت مورد نیاز درخواست شده توسط کاربر (وظایف ارائه شده در خط فرمان یا به عنوان پیشفرض در فایلهای ساخت) را نشان میدهد. این نمودار، رابطه بین وظایف را نشان میدهد، چه به طور صریح در اعلان یک وظیفه و چه بر اساس ورودیها و خروجیهای آن. اگر یک وظیفه ورودیای داشته باشد که خروجی وظیفه دیگری باشد، باید بعد از وظیفه دیگر اجرا شود. این مرحله وظایف قدیمی را به ترتیب تعریف شده در نمودار اجرا میکند. اگر ورودیهای یک وظیفه از آخرین اجرای آن تغییر نکرده باشند، Gradle از آن صرف نظر میکند.
برای اطلاعات بیشتر به چرخه حیات Gradle Build مراجعه کنید.
پیکربندی DSLها
Gradle از یک زبان خاص دامنه (DSL) برای پیکربندی buildها استفاده میکند. این رویکرد اعلانی به جای نوشتن دستورالعملهای گام به گام (دستورالعملی) بر مشخص کردن دادههای شما تمرکز دارد. میتوانید فایلهای build خود را با استفاده از Kotlin یا Groovy بنویسید، اما ما اکیداً استفاده از Kotlin را توصیه میکنیم.
DSLها تلاش میکنند تا مشارکت در یک پروژه را برای همه، متخصصان دامنه و برنامهنویسان، آسانتر کنند و یک زبان کوچک تعریف کنند که دادهها را به روشی طبیعیتر نمایش میدهد. افزونههای Gradle میتوانند DSL را برای پیکربندی دادههای مورد نیاز برای وظایف خود گسترش دهند.
برای مثال، پیکربندی بخش اندرویدِ پروژه شما ممکن است به شکل زیر باشد:
کاتلین
android { namespace = "com.example.app" compileSdk { version = release(36) { minorApiLevel = 1 } } // ... defaultConfig { applicationId = "com.example.app" minSdk { version = release(23) } targetSdk { version = release(36) } // ... } }
گرووی
android { namespace = 'com.example.app' compileSdk { version = release(36) { minorApiLevel = 1 } } // ... defaultConfig { applicationId = 'com.example.app' minSdk { version = release(23) } targetSdk { version = release(36) } // ... } }
در پشت صحنه، کد DSL مشابه کد زیر است:
fun Project.android(configure: ApplicationExtension.() -> Unit) {
...
}
interface ApplicationExtension {
var namespace: String?
fun compileSdk(configure: CompileSdkSpec.() -> Unit) {
...
}
val defaultConfig: DefaultConfig
fun defaultConfig(configure: DefaultConfig.() -> Unit) {
...
}
}
هر بلوک در DSL توسط تابعی نمایش داده میشود که برای پیکربندی خود از یک لامبدا و برای دسترسی به آن از یک ویژگی با همان نام استفاده میکند. این باعث میشود کد موجود در فایلهای ساخت شما بیشتر شبیه یک مشخصه داده به نظر برسد.
وابستگیهای خارجی
سیستم ساخت Maven یک سیستم مشخصات وابستگی ، ذخیرهسازی و مدیریت را معرفی کرد. کتابخانهها در مخازن (سرورها یا دایرکتوریها) ذخیره میشوند و متادیتا شامل نسخه و وابستگیهای آنها به کتابخانههای دیگر است. شما مشخص میکنید که کدام مخازن را جستجو کنید، نسخههای وابستگیهایی را که میخواهید استفاده کنید و سیستم ساخت آنها را در طول ساخت دانلود میکند.
مصنوعات Maven با نام گروه (شرکت، توسعهدهنده و غیره)، نام مصنوع (نام کتابخانه) و نسخه آن مصنوع شناسایی میشوند. این معمولاً به صورت group:artifact:version نمایش داده میشود.
این رویکرد به طور قابل توجهی مدیریت ساخت را بهبود میبخشد. شما اغلب چنین مخازنی را با نام "مخازن Maven" خواهید شنید، اما همه چیز در مورد نحوه بستهبندی و انتشار مصنوعات است. این مخازن و ابردادهها در چندین سیستم ساخت، از جمله Gradle (و Gradle میتواند در این مخازن منتشر شود) دوباره استفاده شدهاند. مخازن عمومی امکان اشتراکگذاری را برای استفاده همه فراهم میکنند و مخازن شرکت، وابستگیهای داخلی را در داخل شرکت نگه میدارند.
شما همچنین میتوانید پروژه خود را به زیرپروژهها (که در اندروید استودیو به عنوان "ماژول" نیز شناخته میشوند) ماژولار کنید ، که میتوانند به عنوان وابستگی نیز استفاده شوند. هر زیرپروژه خروجیهایی (مانند jars) تولید میکند که میتوانند توسط زیرپروژهها یا پروژه سطح بالای شما مصرف شوند. این میتواند با جداسازی بخشهایی که نیاز به بازسازی دارند، زمان ساخت را بهبود بخشد و همچنین مسئولیتها را در برنامه بهتر تفکیک کند.
ما در بخش «افزودن وابستگیهای ساخت» (Add build dependencies) جزئیات بیشتری در مورد نحوه تعیین وابستگیها ارائه خواهیم داد.
ساخت انواع
وقتی یک برنامه اندروید میسازید، معمولاً میخواهید چندین نسخه مختلف بسازید. نسخهها حاوی کدهای متفاوتی هستند یا با گزینههای مختلفی ساخته میشوند و از انواع ساخت و طعمهای محصول تشکیل شدهاند.
انواع ساخت، گزینههای ساخت اعلامشده را تغییر میدهند. بهطور پیشفرض، AGP انواع ساخت «انتشار» و «اشکالزدایی» را تنظیم میکند، اما میتوانید آنها را تنظیم کرده و موارد بیشتری اضافه کنید (شاید برای مرحلهبندی یا آزمایش داخلی).
یک debug build برنامه شما را minify یا obfuscate نمیکند، بلکه سرعت ساخت آن را افزایش میدهد و تمام نمادها را به همان شکل حفظ میکند. همچنین برنامه را به عنوان "debugable" علامتگذاری میکند، آن را با یک کلید اشکالزدایی عمومی امضا میکند و دسترسی به فایلهای برنامه نصب شده روی دستگاه را فعال میکند. این امر امکان بررسی دادههای ذخیره شده در فایلها و پایگاههای داده را هنگام اجرای برنامه فراهم میکند.
یک نسخه آزمایشی، برنامه را بهینه میکند، آن را با کلید انتشار شما امضا میکند و از فایلهای برنامه نصب شده محافظت میکند.
با استفاده از طعمهای محصول ، میتوانید منبع و انواع وابستگیهای موجود برای برنامه را تغییر دهید. برای مثال، ممکن است بخواهید طعمهای "دمو" و "کامل" یا شاید طعمهای "رایگان" و "پولی" برای برنامه خود ایجاد کنید. منبع مشترک خود را در یک دایرکتوری مجموعه منبع "اصلی" مینویسید و منبع را در یک مجموعه منبع به نام طعم، بازنویسی یا اضافه میکنید.
AGP برای هر ترکیبی از نوع ساخت و طعم محصول، انواع مختلفی ایجاد میکند. اگر طعمها را تعریف نکنید، انواع مختلف بر اساس انواع ساخت نامگذاری میشوند. اگر هر دو را تعریف کنید، نوع جدید <flavor><Buildtype> نامگذاری میشود. برای مثال، با انواع ساخت release و debug و طعمهای demo و full ، AGP انواع مختلفی ایجاد میکند:
-
demoRelease -
demoDebug -
fullRelease -
fullDebug
مراحل بعدی
حالا که مفاهیم ساخت را دیدهاید، نگاهی به ساختار ساخت اندروید در پروژه خود بیندازید.