بهینه‌سازی سرعت ساخت

زمان‌های ساخت طولانی فرایند توسعه شما را کند می‌کند. این صفحه برخی‌از تکنیک‌ها را برای کمک به رفع گلوگاه‌های سرعت ساخت ارائه می‌دهد.

فرایند کلی بهبود سرعت ساخت برنامه شما به‌شرح زیر است:

  1. با انجام چند مرحله که بلافاصله برای اکثر پروژه‌های «استودیو Android» مفید است، پیکربندی ساخت خود را بهینه‌سازی کنید.
  2. برای شناسایی و تشخیص برخی‌از گلوگاه‌های پیچیده‌تر که ممکن است مختص پروژه یا ایستگاه کاری شما باشند، ساختمان خود را نمایه‌بندی کنید.

هنگام توسعه برنامه‌تان، درصورت امکان آن را در دستگاهی با Android نسخه ۷.۰ (سطح میانای برنامه‌سازی کاربردی ۲۴) یا بالاتر مستقر کنید. نسخه‌های جدیدتر پلاتفرم Android سازوکارهای بهتری برای ارسال به‌روزرسانی‌ها به برنامه شما پیاده‌سازی می‌کنند، مانند زمان اجرای Android (ART) و پشتیبانی بومی از چندین فایل DEX.

توجه: پس‌از اولین ساخت تمیز، ممکن است متوجه شوید که ساخت‌های بعدی، چه تمیز و چه افزایشی، حتی بدون استفاده از هیچ‌یک از بهینه‌سازی‌های شرح‌داده‌شده در این صفحه، بسیار سریع‌تر اجرا می‌شوند. دلیل این امر این است که کارگزار Gradle دوره «گرم‌شدن» دارد که در آن عملکرد افزایش می‌یابد—مشابه دیگر فرایندهای JVM.

بهینه‌سازی پیکربندی ساخت

این نکات را برای بهبود سرعت ساخت پروژه Android Studio دنبال کنید.

ابزارهایتان را به‌روز نگه دارید

ابزارهای Android تقریباً با هر به‌روزرسانی، بهینه‌سازی‌های ساخت و ویژگی‌های جدیدی دریافت می‌کنند. برخی‌از نکته‌های این صفحه فرض می‌کنند که از جدیدترین نسخه استفاده می‌کنید. برای بهره‌مندی از جدیدترین بهینه‌سازی‌ها، موارد زیر را به‌روز نگه دارید:

به‌جای kapt از KSP استفاده کنید

«ابزار پردازش شرح Kotlin» (kapt) به‌طور قابل‌توجهی از «پردازشگر نماد Kotlin» (KSP) کُندتر است. اگر درحال نوشتن منبع Kotlin حاشیه‌گذاری‌شده هستید و از ابزارهایی استفاده می‌کنید که حاشیه‌گذاری‌ها را پردازش می‌کنند (مثل Room) که از KSP پشتیبانی می‌کند، بهتر است به KSP انتقال دهید.

از گردآوری منابع غیرضروری اجتناب کنید

از گردآوری و بسته‌بندی منابعی که آزمایش نمی‌کنید، مثل بومی‌سازی‌های زبان اضافی و منابع تراکم صفحه، خودداری کنید. درعوض، فقط یک منبع زبان و تراکم صفحه برای طعم «توسعه‌دهنده» خود مشخص کنید، همان‌طور که در نمونه زیر نشان داده شده است:

شیک

android {
    ...
    productFlavors {
        dev {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations "en", "xxhdpi"
        }
        ...
    }
}

کاتلین

android {
    ...
    productFlavors {
        create("dev") {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations("en", "xxhdpi")
        }
        ...
    }
}

آزمایش کنید که «درگاه افزایه Gradle» را در آخر قرار دهید

در Android، همه افزایه‌ها در مخازن google() و mavenCentral() یافت می‌شوند. بااین‌حال، ساختار شما ممکن است به افزایه‌های طرف سوم نیاز داشته باشد که بااستفاده از سرویس gradlePluginPortal() حل‌وفصل می‌شوند.

‫Gradle مخزن‌ها را به ترتیبی که اعلام شده‌اند جستجو می‌کند، بنابراین اگر مخزن‌های فهرست‌شده در ابتدا حاوی بیشتر افزایه‌ها باشند، عملکرد ساخت بهبود می‌یابد. بنابراین، ورودی gradlePluginPortal() را با قرار دادن آن در انتهای بلوک مخزن در فایل settings.gradle امتحان کنید. در بیشتر موارد، این کار تعداد جستجوهای افزونه زائد را به‌حداقل می‌رساند و سرعت ساخت را بهبود می‌بخشد.

برای کسب اطلاعات بیشتر درباره اینکه Gradle چگونه در چندین مخزن پیمایش می‌کند، به اعلام چندین مخزن در اسناد Gradle مراجعه کنید.

استفاده از مقادیر پیکربندی ساخت ایستا با ساخت اشکال‌زدایی

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

استفاده از کدهای نسخه پویا، نام‌های نسخه، منابع، یا هر منطق ساخت دیگری که فایل مانیفست را تغییر می‌دهد، هر بار که می‌خواهید تغییری را اجرا کنید، حتی اگر تغییر واقعی ممکن است درغیراین‌صورت فقط به تعویض داغ نیاز داشته باشد، به ساخت کامل برنامه نیاز دارد. اگر پیکربندی ساخت شما به چنین دارایی‌های پویا نیاز دارد، آن دارایی‌ها را به گونه‌های ساخت انتشار خود محدود کنید و مقادیر را برای ساخت‌های اشکال‌زدایی خود ثابت نگه دارید، همان‌طور که در نمونه زیر نشان داده شده است:

  ...
  // Use a filter to apply onVariants() to a subset of the variants.
  onVariants(selector().withBuildType("release")) { variant ->
      // Because an app module can have multiple outputs when using multi-APK, versionCode
      // is only available on the variant output.
      // Gather the output when we are in single mode and there is no multi-APK.
      val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

      // Create the version code generating task.
      val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
          it.outputFile.set(project.layout.buildDirectory.file("versionCode${variant.name}.txt"))
      }

      // Wire the version code from the task output.
      // map will create a lazy Provider that:
      // 1. Runs just before the consumer(s), ensuring that the producer (VersionCodeTask) has run
      //    and therefore the file is created.
      // 2. Contains task dependency information so that the consumer(s) run after the producer.
      mainOutput.versionCode.set(versionCodeTask.flatMap { it.outputFile.map { it.asFile.readText().toInt() } })
  }
  ...

  abstract class VersionCodeTask : DefaultTask() {

    @get:OutputFile
    abstract val outputFile: RegularFileProperty

    @TaskAction
    fun action() {
        outputFile.get().asFile.writeText("1.1.1")
    }
  }

برای آشنایی با نحوه تنظیم کد نسخه پویا در پروژه‌تان، دستورالعمل setVersionsFromTask را در GitHub ببینید.

استفاده از نسخه‌های وابستگی ایستا

وقتی وابستگی‌ها را در فایل‌های build.gradle اعلام می‌کنید، از استفاده از شماره‌های نسخه پویا (آن‌هایی که در انتها علامت به‌علاوه دارند، مثل 'com.android.tools.build:gradle:2.+') خودداری کنید. استفاده از شماره‌های نسخه پویا می‌تواند باعث به‌روزرسانی‌های غیرمنتظره نسخه، دشواری در رفع تفاوت‌های نسخه، و کندتر شدن ساخت‌ها به‌دلیل بررسی به‌روزرسانی‌ها توسط Gradle شود. درعوض از شماره‌های نسخه ایستا استفاده کنید.

ایجاد واحدهای کتابخانه

در برنامه‌تان به‌دنبال کدی بگردید که بتوانید آن را به واحد کتابخانه Android تبدیل کنید. با این روش، سیستم ساخت می‌تواند فقط واحدهایی را که تغییر داده‌اید گردآوری کند و بروندادهای آن‌ها را برای ساخت‌های آینده در حافظه نهان ذخیره کند. وقتی این بهینه‌سازی را فعال می‌کنید، واحدبندی همچنین باعث می‌شود اجرای موازی پروژه مؤثرتر شود.

ایجاد تکلیف برای منطق ساخت سفارشی

پس‌از اینکه نمایه ساخت ایجاد کردید، اگر نمایه ساخت نشان داد که بخش نسبتاً طولانی از زمان ساخت در مرحله **پیکربندی پروژه‌ها** صرف می‌شود، برنامه‌های build.gradle را مرور کنید و به‌دنبال کد برای افزودن به تکلیف سفارشی Gradle بگردید. با انتقال برخی‌از منطق ساخت به تکلیف، کمک می‌کنید تا مطمئن شوید تکلیف فقط درصورت نیاز اجرا می‌شود، نتایج می‌تواند برای ساخت‌های بعدی ذخیره شود، و منطق ساخت درصورت فعال کردن اجرای موازی پروژه واجدشرایط اجرای موازی می‌شود. برای کسب اطلاعات بیشتر درباره تکالیف مربوط به منطق ساخت سفارشی، سند رسمی Gradle را بخوانید.

نکته: اگر ساختار شما شامل تعداد زیادی تکلیف سفارشی است، شاید بخواهید با ایجاد کلاس‌های تکلیف سفارشی، فایل‌های build.gradle را مرتب کنید. کلاس‌هایتان را به فهرست راهنمای project-root/buildSrc/src/main/groovy/ اضافه کنید؛ Gradle به‌طور خودکار آن کلاس‌ها را در مسیر کلاس برای همه فایل‌های build.gradle در پروژه‌تان اضافه می‌کند.

تبدیل تصاویر به WebP

WebP قالب فایل تصویری است که فشرده‌سازی پراتلاف (مثل JPEG) و همچنین شفافیت (مثل PNG) را ارائه می‌دهد. WebP می‌تواند فشرده‌سازی بهتری نسبت به JPEG یا PNG ارائه دهد.

کاهش اندازه فایل تصویر بدون نیاز به انجام فشرده‌سازی زمان ساخت می‌تواند سرعت ساخت را افزایش دهد، به‌ویژه اگر برنامه شما از منابع تصویری زیادی استفاده کند. بااین‌حال، ممکن است هنگام فشرده‌سازی تصاویر WebP متوجه افزایش اندکی در استفاده از CPU دستگاه شوید. از «استودیو Android» برای تبدیل آسان تصاویر به WebP استفاده کنید.

غیرفعال کردن فشرده‌سازی PNG

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

اگر از افزایه Android Gradle نسخه ۳.۰.۰ یا بالاتر استفاده می‌کنید، فشرده‌سازی PNG به‌طور پیش‌فرض برای نوع ساخت «اشکال‌زدایی» غیرفعال است. برای غیرفعال کردن این بهینه‌سازی برای انواع دیگر ساخت، موارد زیر را به فایل build.gradle اضافه کنید:

شیک

android {
    buildTypes {
        release {
            // Disables PNG crunching for the "release" build type.
            crunchPngs false
        }
    }
}

کاتلین

android {
    buildTypes {
        getByName("release") {
            // Disables PNG crunching for the "release" build type.
            isCrunchPngs = false
        }
    }
}

چون انواع ساخت یا نسخه‌های ویژه محصول این ویژگی را تعریف نمی‌کنند، باید این ویژگی را به‌صورت دستی روی true تنظیم کنید تا نسخه پخش برنامه‌تان ساخته شود.

آزمایش با جمع‌آوری‌کننده زباله موازی JVM

با پیکربندی بهینه JVM garbage collector که Gradle از آن استفاده می‌کند، می‌توان عملکرد ساخت را بهبود بخشید. درحالی‌که JDK 8 پیکربندی شده است تا به‌طور پیش‌فرض از جمع‌آوری‌کننده زباله موازی استفاده کند، JDK 9 و نسخه‌های بالاتر پیکربندی شده‌اند تا از جمع‌آوری‌کننده زباله G1 استفاده کنند.

برای بهبود احتمالی عملکرد ساخت، توصیه می‌کنیم ساخت‌های Gradle خود را با جمع‌آوری زباله موازی آزمایش کنید. در gradle.properties، موارد زیر را تنظیم کنید:

org.gradle.jvmargs=-XX:+UseParallelGC

اگر گزینه‌های دیگری ازقبل در این فیلد تنظیم شده است، گزینه جدیدی اضافه کنید:

org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC

برای اندازه‌گیری سرعت ساخت با پیکربندی‌های مختلف، به نمایه ساخت مراجعه کنید.

افزایش اندازه پشته JVM

اگر متوجه شدید ساخت‌ها کند است، و به‌ویژه جمع‌آوری پسماند بیش‌از ۱۵٪ از زمان ساخت را در نتایج Build Analyzer شما می‌گیرد، باید اندازه پشته ماشین مجازی جاوا (JVM) را افزایش دهید. در فایل gradle.properties، حد را روی ۴، ۶، یا ۸ گیگابایت تنظیم کنید همان‌طور که در مثال زیر نشان داده شده است:

org.gradle.jvmargs=-Xmx6g

سپس بهبود سرعت ساخت را آزمایش کنید. آسان‌ترین راه برای تعیین اندازه بهینه پشته این است که حد را مقدار کمی افزایش دهید و سپس بهبود کافی در سرعت ساخت را آزمایش کنید.

اگر از جمع‌کننده زباله موازی JVM نیز استفاده می‌کنید، کل خط باید به‌این شکل باشد:

org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g

می‌توانید با روشن کردن پرچم HeapDumpOnOutOfMemoryError، خطاهای حافظه JVM را تجزیه‌وتحلیل کنید. با انجام این کار، JVM درصورت اتمام حافظه، رونوشت پشته تولید خواهد کرد.

استفاده از کلاس‌های R غیرانتقالی

از کلاس‌هایR غیرمتعدی استفاده کنید تا ساخت‌های سریع‌تری برای برنامه‌های دارای چند ماژول داشته باشید. این کار با اطمینان از اینکه کلاس R هر ماژول فقط حاوی ارجاع به منابع خودش است و ارجاعی از وابستگی‌هایش دریافت نمی‌کند، به جلوگیری از تکرار منابع کمک می‌کند. این کار منجر به ساخت سریع‌تر و مزایای مربوط به اجتناب از کامپایل می‌شود. این رفتار پیش‌فرض در افزایه Android Gradle نسخه ۸.۰.۰ و بالاتر است.

از Android Studio Bumblebee شروع می‌شود، کلاس‌های R غیرانتقالی به‌طور پیش‌فرض برای پروژه‌های جدید روشن است. برای پروژه‌هایی که با نسخه‌های قبلی «استودیو Android» ایجاد شده‌اند، آن‌ها را به‌روز کنید تا با رفتن به بازسازی کد > انتقال به کلاس‌های R غیرمتعدی از کلاس‌های غیرمتعدی R استفاده کنند.

برای آشنایی بیشتر با منابع برنامه و کلاس R، به نمای کلی منابع برنامه مراجعه کنید.

استفاده از کلاس‌های R غیرثابت

از فیلدهای کلاس R غیرثابت در برنامه‌ها و آزمایش‌ها استفاده کنید تا افزایش کامپایل Java را بهبود دهید و امکان کوچک کردن دقیق‌تر منابع را فراهم کنید. فیلدهای کلاس R همیشه برای کتابخانه‌ها ثابت نیستند، زیرا منابع هنگام بسته‌بندی APK برای برنامه یا آزمایشی که به آن کتابخانه وابسته است شماره‌گذاری می‌شوند. این رفتار پیش‌فرض در «افزایه Android Gradle» نسخه ۸.۰.۰ و بالاتر است.

پرچم Jetifier را غیرفعال کنید

ازآنجایی‌که اکثر پروژه‌ها مستقیماً از کتابخانه‌های AndroidX استفاده می‌کنند، می‌توانید پرچم Jetifier را برای عملکرد بهتر ساخت بردارید. برای برداشتن پرچم Jetifier، android.enableJetifier=false را در فایل gradle.properties تنظیم کنید.

«تحلیل‌کننده ساخت» می‌تواند بررسی کند که آیا پرچم را می‌توان به‌طور ایمن برداشت تا پروژه شما عملکرد ساخت بهتری داشته باشد و از کتابخانه‌های «پشتیبانی Android» که دیگر نگهداری نمی‌شوند به جای دیگری منتقل شود. برای کسب اطلاعات بیشتر درباره «تحلیل‌گر ساخت»، به عیب‌یابی عملکرد ساخت مراجعه کنید.

استفاده از حافظه نهان پیکربندی

حافظه نهان پیکربندی به Gradle اجازه می‌دهد اطلاعات مربوط به گراف وظایف ساخت را ضبط کند و در ساخت‌های بعدی از آن استفاده مجدد کند، بنابراین Gradle مجبور نیست کل ساخت را دوباره پیکربندی کند.

برای فعال کردن حافظه نهان پیکربندی، این مراحل را دنبال کنید:

  1. بررسی کنید که همه افزایه‌های پروژه سازگار باشند.

    از Build Analyzer استفاده کنید تا بررسی کنید آیا پروژه‌تان با حافظه پنهان پیکربندی سازگار است یا نه. «تجزیه‌وتحلیل‌گر ساخت» مجموعه‌ای از ساخت‌های آزمایشی را اجرا می‌کند تا تعیین کند که آیا ویژگی می‌تواند برای پروژه روشن شود یا نه. برای فهرست افزونه‌های پشتیبانی‌شده، به مسئله شماره ۱۳۴۹۰ مراجعه کنید.

  2. کد زیر را به فایل gradle.properties اضافه کنید:

      org.gradle.configuration-cache=true
      # Use this flag carefully, in case some of the plugins are not fully compatible.
      org.gradle.configuration-cache.problems=warn

وقتی حافظه نهان پیکربندی فعال باشد، اولین‌باری که پروژه را اجرا می‌کنید برونداد ساخت می‌گوید Calculating task graph as no configuration cache is available for tasks. در اجراهای بعدی، برونداد ساخت می‌گوید Reusing configuration cache.

برای کسب اطلاعات بیشتر درباره حافظه نهان پیکربندی، پست وبلاگ کاوش عمیق حافظه نهان پیکربندی و مستندات Gradle درباره حافظه نهان پیکربندی را ببینید.

مشکلات حافظه نهان پیکربندی که در Gradle 8.1 و افزایه Android Gradle 8.1 معرفی شده است

حافظه پنهان پیکربندی در Gradle 8.1 پایدار شد و ردیابی میانای برنامه‌سازی کاربردی فایل را معرفی کرد. تماس‌هایی مثل File.exists()، File.isDirectory()، و File.list() توسط Gradle ضبط می‌شود تا فایل‌های ورودی پیکربندی را پیگیری کند.

«افزایه Android Gradle»‏ (AGP) نسخه ۸.۱ از این File API برای برخی‌از فایل‌هایی استفاده می‌کند که Gradle نباید آن‌ها را به‌عنوان ورودی‌های حافظه نهان درنظر بگیرد. این کار هنگام استفاده با Gradle 8.1 و نسخه‌های بالاتر باعث باطل شدن حافظه نهان اضافی می‌شود و عملکرد ساخت را کند می‌کند. موارد زیر به‌عنوان ورودی‌های حافظه نهان در AGP 8.1 درنظر گرفته می‌شوند:

ورودی Issue Tracker رفع‌شده در
$GRADLE_USER_HOME/android/FakeDependency.jar مشکل شماره ۲۸۹۲۳۲۰۵۴ AGP 8.2
برونداد cmake مشکل شماره ۲۸۷۶۷۶۰۷۷ AGP 8.2
$GRADLE_USER_HOME/.android/analytics.settings مشکل شماره ۲۷۸۷۶۷۳۲۸ ‫AGP 8.3

اگر از این میاناهای برنامه‌سازی کاربردی یا افزایه‌ای که از این میاناهای برنامه‌سازی کاربردی استفاده می‌کند استفاده کنید، ممکن است در زمان ساخت با پسرفت مواجه شوید، زیرا برخی‌از منطق ساخت که از این میاناهای برنامه‌سازی کاربردی استفاده می‌کنند می‌توانند باعث باطل شدن حافظه نهان اضافی شوند. لطفاً برای بحث درباره این الگوها و نحوه اصلاح منطق ساخت، یا غیرفعال کردن موقت ردیابی میانای برنامه‌سازی کاربردی فایل، به بهبودها در ردیابی ورودی پیکربندی ساخت مراجعه کنید.