گریدل چه جوری جادو می کنه (شکافتن فایل های گریدل)

کار کردن با گریدل واسه برنامه نویسایی که تازه کار هستن کمی ترسناکه. چون خیلی باگ میده، همش باید آنلاین باشی و فیلترشکن خوب داشته باشی، چون کمی پیچیده به نظر میاد و محتواش رو نمی تونن بخونن بنابراین سعی می کنن ازش فاصله بگیرن و باهاش درگیر نشن. توی نوشته های قبلی به طور کلی گریدل رو معرفی کردیم و در مورد اینکه چه جوری فرآیند بیلد رو اتوماتیک می کنه گفتیم. اگه مطالعه نکردین می تونید به لینک زیر مراجعه کنید: https://vrgl.ir/pyKOW بعدم که زبون برنامه نویسی گرووی رو معرفی کردیم که توی گریدل استفاده میشه واسه پیاده کردن تسک های گریدل. اگه با گرووی آشنا نیستین لطفا مقاله قبلی بنده که به معرفی گرووی پرداختم رو مطالعه بفرمایید: https://vrgl.ir/1s9Z3 از اینجا به بعد می خواییم کمی موشکافانه تر به گریدل نگاه کنیم و ببینیم دقیقا چه تنظیمات و کنترل هایی برای خروجی گرفتن از پروژهای اندروید تو گریدل وجود داره.اجزای گریدل تو پروژه های اندرویدی:برای اینکه بفهمیم دقیقا گریدل چه جوری کار می کنه لازمه همه چیزایی که تو خروجی اپ ما تاثیر میذاره رو بشناسیم. یعنی اینکه علاوه بر کد کاتلین و جاوایی که نوشتیم یه سری تنظیمات توی فایل های گریدل پروژه هستن که به ما کمک می کنن تا خروجی رو کنترل کنیم. این تنظیمات عبارتند از:Built Type:بیلد تایپ شامل پارامتر های برای بیلد کردنه که گریدل استفاده می کنه. کلاژر بیلد تایپ توی فایل گریدل ماژول (مثلا app) استفاده میشه.از جمله پارامتر هاش میشه کنترل Proguard رو معرفی کرد. می تونید کلید sign کردن اپ رو هم اینجا تعریف کنید.⬇️ توی نمونه کد پایین ما یه بیلد تایپ تستی رو می بینیم:android { ... buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } ... }Product Flavor:در واقع Product Flavor ها ورژن های مختلف اپ رو تعریف می کنن اما این ورژن های مختلف به معنی نسل های اپ نیست بلکه به معنی خروجی های مختلف از اپ هست. مثالی که همیشه ازش استفاده میشه اینه که اگه اپ شما قرار باشه دو نسخه پولی و رایگان داشته باشه که کنار هم روی هر دیوایسی نصب بشن از Product Flavor استفاده میشه.به دلیل اختیاری بودن Product Flavor، به صورت پیش فرض توی گریدل نیست و اگه بهش نیاز داشته باشید باید اضافه کنید که جای اضافه کردنش توی فایل گریدل ماژول ها داخل کلاژر android هست.⬇️ تو نمونه کد پایین ما چندتا Product Flavor تستی که تعریف شدن رو می بینیم:android { ... productFlavors { demo { applicationIdSuffix &quot.demo&quot versionNameSuffix &quot-demo&quot } full { applicationIdSuffix &quot.full&quot versionNameSuffix &quot-full&quot } } }تو نمونه کد بالا نمونه تعریف Product Flavor رو می بینید که دو تا نسخه دمو و فول رو تعریف می کنه. اون پارامترها به عنوان مثال پسوند اپ آی دی و ورژن هستند برای اینکه دو تا خروجی کنار هم نصب بشن.Build Variant:بیلد ورینت در واقع محصول مشترک Build Type و Product Flavor هست که گریدل برای بیلد استفاده می کنه و برای هر دو مد دیباگ و ریلیز استفاده میشه.نکته اینه که شما مستقیما با Build Variant کار نمی کنید و در واقع این گریدل هست که برای کنترل اون از Build Type و Product Flavor استفاده می کنه. خلاصه اینکه حاصل کار با Build Type و Product Flavor میشه Build Variant.⬇️ تب Build Variant رو تو عکس پایین ببینید:توی عکس بالا، تصویر تب Build Variants رو می بینید، که ماژول app رو به صورت جدول نشون میده و توی اون ماژول 4 تا Build Variant داریم.چه جوری این اتفاق می افته؟ اول اینکه ما به صورت پیش فرض تو پروژه دو تا Build Type دیباگ و ریلیز رو داریم. از طرفی دو تا Product Flavor فول و دمو رو هم داریم، که در نتیجه این ارتباط میشه چهار تا بیلد ورینت demoDebug و demoRelease و fullDebug و fullRelease.ورودی های Manifest:اینجا با کلیت کار Manifest کاری نداریم (مثلا معرفی المان های اصلی اپ از جمله اکتیویتی ها و سرویس ها و برادکست ریسیور ها) اما با این کار داریم که Manifest شامل پارامترهایی هست که تو فرآیند بیلد اپ تاثیر میذاره. از جمله این پارامتر ها minimum SDK version و target SDK version هستن که توی Build Type هم قابل دسترس هستند.البته لازم به ذکره که اولویت مقادیر Build Type نسبت به پارامتر های Manifest بیشتر هست و کامپایلر به مقادیر Build Type ارجحیت میده. در پروژه ای که شامل چندتا منیفست باشه، تنظیمات موجود در این منیفست ها با هم merge میشن که ادامه دادنش در این مقاله نمی گنجه.<?xml version=&quot1.0&quot encoding=&quotutf-8&quot?> <manifest xmlns:android=&quothttp://schemas.android.com/apk/res/android&quot package=&quotir.testapplication&quot ... android:compileSdkVersion=&quot10&quot android:compileSdkVersionCodename=&quot30.0.3&quot android:versionName=&quot1.0.0&quot> <application> ... </application> </manifest>Dependencyها:پر استفاده ترین بخش توی پروژه ها واسه ما دوولوپرا همین بخش Dependencyها هستش. بقیه قسمت های گریدل یکبار یا دوبار تنظیم میشن تو کل پروژه و دیگه تقریبا باهاشون کاری نداریم (به ندرت شاید بیشتر بشه)، هرچند بعضی پروژه ها هم اصلا مارو درگیر تنظیمات مثلا Build Type و Product Flavor نمی کنن.اما این دیپندنسی ها چیزی هستن که ما در فرآیند توسعه اپ مدام باهاشون کار داریم. اعم از فایل های jar یا لایبرری هایی که از طریق فایل سیستم به پروژه اضافه کردیم یا لایبرری هایی که از طریق آدرس ریموت به پروژه اضافه کردیم. گریدل همه رو کنترل می کنه و تو پروژه اضافه می کنه.اگه قرار بود دستی این کار رو انجام بدیم باید دونه دونه بایت کدهاشون رو دانلود می کردیم و تو پروژه جا میدادیم که فرآید خیلی اذیت کنی میشد.⬇️ نمونه کد پایین، دیپندنسی های پیش فرض تو یه پروژه جدید رو نشون میده، که تو فایل build.gradle هر ماژولی می تونه باشه:plugins { ... } android { ... } dependencies { implementation &quotorg.jetbrains.kotlin:kotlin-stdlib:$kotlin_version&quot implementation 'androidx.core:core-ktx:1.5.0' implementation 'androidx.appcompat:appcompat:1.3.0' implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation &quotandroidx.drawerlayout:drawerlayout:1.1.1&quot testImplementation 'junit:junit:4.+' androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0' }Signing:خیلی از ماها معمولا برای خروجی گرفتن از پروژه، از منو Build توی اندروید استودیو استفاده می کنیم. هم خروجی دیباگ می گیریم (بدون ساین کردن پروژه با کلید تعریف شده خودمون) و هم خروجی ریلیز (با استفاده از کلید تعریف شده خودمون).اما با استفاده از signingConfigs هم میشه از پروژه خروجی گرفت. این تنظیمات تو کلاژر android تو فایل build.gradle ماژول اپ استفاده میشه.به این کد توجه کنید:android { ... signingConfigs { release { storeFile file(&quotrelease-key.keystore&quot) storePassword 'passwotd' keyAlias 'alias' keyPassword 'password' } } buildTypes { release { ... signingConfig signingConfigs.release } debug { debuggable true } } }تو نمونه کد بالا ما برای ساین کردن اپ کلید تعریف کردیم و کافیه که تسک assembleRelease رو تو ترمینال مثل کد پایین صدا کنیم تا از پروژه خروجی ریلیز بگیریم:./gradlew assembleReleaseخاصیت این کار اینه که کلید پروژه رو داخل خود پروژه تعریف کردیم و همیشه در دسترس هست. اما نکته منفی این کار اینه که کلید های ساین اپ در دسترس هر کسی که به کدها دسترسی داشته باشه هست و این ممکنه برای جاهایی که تیم انتشار از تیم توسعه جدا هستند مورد پذیرش نباشه.ریز کردن Code و Resource:با استفاده از تنظیمات proguard می شه به گریدل حالی کرد که موقع بیلد کردن از Rule های مختلفی برای Build Variantهای مختلف استفاده کنه. مثلا به تنظیمات زیر نگاه کنید:android { ... buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } productFlavors { flavor1 { ... } flavor2 { proguardFile 'flavor2-rules.pro' } } }اینجا ما واسه flavor2 یه فایل رول جدید تعریف کردیم که به همین شکل می تونه یه فایل متفاوت برای flavor1 تعریف بشه.پشتیبانی از APKهای مختلف:ما می تونیم توی فایل build.gradle ماژول اپ، تنظیماتی رو تعریف کنیم که اپ ما واسه چه دنسیتی (تعداد پیکسل ها توی یک اینچ مربع از صفحه نمایش) قراره استفاده بشه. مثلا xhdpi یا xxxhdpi یا غیره. بنابراین فقط از ریسورس های مناسب اون سایز تو بیلد استفاده میشه. به طور مثال به تیکه کد زیر توجه کنید:android { ... splits { density { enable true exclude &quotldpi&quot, &quotxxhdpi&quot, &quotxxxhdpi&quot compatibleScreens 'small', 'normal', 'large', 'xlarge' } } }توی تیکه کد بالا که توی فایل build.gradle ماژول تعریف کردیم، با استفاده از کلاژر splits یه دنسیتی تعریف کردیم و گفتیم خروجی برای کدوم کیفیت های صفحه نمایش فعال باشه. همین طور به طور مشابه می تونیم برای نوع پردازنده هم خروجی رو فعال و غیر فعال کنیم.فایل های گریدل:تا اینجای کار تنظیمات اصلی که توی گریدل استفاده میشن برای کنترل بیلد و خروجی پروژه رو گفتیم. حالا اینجا می خواییم فایل های گریدل که تو یه پروژه اندرویدی استفاده میشن رو توضیح بدیم که چیا هستن و چه کاربردی دارن.settings.gradle:این فایل که توی روت اصلی پروژه هست، به گریدل میگه که کدوم ماژول ها توی فرآیند بیلد باید لحاظ بشن. به طور نرمال تو اکثر پروژه ها فقط ماژول اپ موجود هست، اما توی پروژه های چند ماژوله باید تو این فایل درج بشه که همه ماژول ها یا اونایی که نیاز داریم رو در نظر بگیره. مثلا محتویات داخل این فایل این شکلیه:include ':simpleModule' include ':app' rootProject.name = &quotroot_name&quotمن تو این پروژه به غیر از ماژول اصلی app یه ماژول دیگه به اسم simpleModule دارم که به پروژه اضافه شده و تو فرآیند بیلد میاد.build.gradle سطح ماژول:این فایل توی فولدر هر ماژولی هست و به تنظیمات مخصوص اون ماژول می پردازه، از جمله تنظیمات Build Type و Product Flavor و Proguard. تنظیماتی که تو این فایل ست میشه اگر در build.gradle پروژه هم باشه، روی اونها override میشه و برای بیلد کردن ارجحیت داره. یه نمونه تنظیمات موجود توی این فایل رو تو نمونه کد پایین آوردم. بعضی تنظیمات آوردنشون اختیاری هستش، که به productFlavors میشه اشاره کرد:apply plugin: 'com.android.application' android { compileSdkVersion 28 buildToolsVersion &quot30.0.2&quot defaultConfig { applicationId 'com.example.myapp' minSdkVersion 15 targetSdkVersion 28 versionCode 1 versionName &quot1.0&quot } buildTypes { release { minifyEnabled true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } flavorDimensions &quottier&quot productFlavors { free { dimension &quottier&quot applicationId 'com.example.myapp.free' } paid { dimension &quottier&quot applicationId 'com.example.myapp.paid' } } } dependencies { implementation project(&quot:lib&quot) implementation 'com.android.support:appcompat-v7:28.0.0' implementation fileTree(dir: 'libs', include: ['*.jar']) }build.gradle سطح پروژه:این فایل هم توی روت اصلی پروژه واقع شده و به بخشی از تنظیمات گریدل اشاره داره که تو همه ماژول های پروژه یکسان هستند، از جمله ریپازیتوری های لایبرری ها و ورژن گریدل.شما می تونید توی این فایل متغییر هایی رو تعریف کنید که توی سایر بخش های پروژه به طور مشترک و global در دسترس باشن.⬇️ تیکه کد پایین یک نمونه از محتویات این فایل به همراه تعدادی متغیر گلوبال هست:buildscript { repositories { google() jcenter() } dependencies { classpath 'com.android.tools.build:gradle:4.2.0' } } allprojects { repositories { google() jcenter() } } ext { supportLibVersion = &quot28.0.0&quot }تو نمونه کد بالا یه متغیر گلوبال به اسم supportLibVersion تعریف کردم که مقدار ورژن لایبرری ساپورت رو تو خودش نگه میداره و هرجا لازم داشته باشم می تونم با صدا زدن کد نمونه زیر ازش استفاده کنم:... dependencies { implementation &quotcom.android.support:appcompatv7: ${rootProject.ext.supportLibVersion}&quot }مثلا تو همه ماژولا از این لایبرری استفاده می کنم و می خوام همه جا ورژن یکسان باشه و از یکجا تغییر کنه.gradle.properties:تو این فایل تنظیمات خود گریدل از جمله سایز Heap و اینکه از اندروید استودیو استفاده بشه یا نه و jetifier فعال باشه یا نه ست میشن.local.properties:تو این فایل متغیر های محیطی که گریدل ازشون برای بیلد کردن استفاده می کنه تنظیم میشن. متغیر های محیطی از جمله آدرس SDK , NDK و CMake هستند.فرآیند Build اپ چه جوری شروع میشه:زمانی که شما دکمه Run رو تو اندروید استودیو می زنید در واقع به اندروید استودیو می گید که تسک assembleDebug رو از ماژول اصلی کانفیگ (که در اکثر مواقع همون ماژول app هست) اجرا کنه و این تسک، اول شروع می کنه به اجرای تسک هایی که پیش نیازش هستن و بعد اجرای خودش و در نهایت تسک هایی که بعد از خودش باید اجرا بشن رو اجرا می کنه.هرچند دکمه ران در کنار اجرای تسک assembleDebug یه سری آماده سازی های دیگه رو هم اجرا می کنه اما در واقع اصل قضیه شروع اجرای همین تسک هست. حالا اگه بخوایید از پروژه خروجی APK بگیرید باید تسک assembleRelease رو صدا کنید تا اجرا بشه و خروجی بده.عکس زیر کل فرآیند بیلد و ران کردن اپ رو با تمام جزئیاتش نشون میده:راهنمای عکس به قدر کافی واضحه و اینکه جهت فلش ها نشون دهنده جهت فرآیند و شروع و پایان اون هست. فرآیند بیلد اپ با چک کردن و کامپایل کردن ریسورس ها و assetها شروع میشه. بخش اول ارور هایی که موقع ران کردن اپ یا خروجی گرفتن براتون پیش میاد برای همین بخشه که مثلا یه ریسورسی ناموجوده اما صدا زده شده یا اینکه اصطلاحا ناقص و ناخواناست.بعد از ریسورس ها نوبت کلاس های جاوا و کاتلینه که به بایت کدها تبدیل بشن. اینجا هم ارور های نام آشنایی پیش میان که دیگه خودتون استادین. ارور های سینتکسی که نذاشتن سمیکالن نام آشنا ترینشون هست.بعد از کدهای جاوا و کاتلین نوبت به پروگارد کردن و پکیجینگ هست. تو این بخش ارور های مربوط به dex و proguard و obfuscate پیش میاد که مثلا ماژول ها نمی تونن باهم مچ بشن یا اینکه یه فایلی بزرگتر از سایز قابل دگز کردن هست. در نهایت هم که پکیج بدست اومده از مرحله قبل باید sign بشه که در نهایت میشه ازش استفاده و منتشر کرد.graمطالب بالا خلاصه ای از پیج خود اندروید دولوپر بود که توی لینک زیر براتون قابل دسترس هست. البته چون اندروید دولوپر مارو تحریم کرده، لینک رو همین جوری آوردم: https://developer.android.com/studio/build ممنون از اینکه وقت گذاشتین و مقاله های سری گریدل بنده رو مطالعه کردین و ممنون از اینکه با لایک هاتون منو تشویق به ادامه راه می کنید. لطفا اگه مطلبی یا سوالی مد نظرتون هست تو کامنتا برام بذارین تا در موردش صحبت کنیم.