אופטימיזציה של מהירות ה-build

זמני build ארוכים מאטים את תהליך הפיתוח. בדף הזה מפורטות כמה שיטות שיעזרו לכם פתרון צווארי בקבוק במהירות ה-build.

התהליך הכללי של שיפור מהירות ה-build של האפליקציה הוא:

  1. אופטימיזציה של תצורת ה-build שלבים שיועילו באופן מיידי לרוב הפרויקטים ב-Android Studio.
  2. ליצור פרופיל של ה-build כדי לזהות ולאבחן חלק של צווארי הבקבוק המורכבים יותר שעשויים להיות ספציפיים לפרויקט או לתחנת העבודה שלכם.

בזמן פיתוח האפליקציה, יש לפרוס במכשיר Android 7.0 (רמת API 24) ואילך ככל האפשר. גרסאות חדשות יותר של בפלטפורמת Android מיושמת מנגנון טוב יותר להעברת עדכונים לאפליקציה. כמו Android זמן ריצה (ART) ותמיכה מובנית בקובצי DEX מרובים.

הערה: אחרי ה-build הנקי הראשון, ייתכן שתבחינו בכך מותאמות אישית, גם נקייה וגם מצטברות, מניבות ביצועים מהירים הרבה יותר אפילו בלי להשתמש הצעות האופטימיזציה שמתוארות בדף הזה. זה בגלל שהדימון של Gradle יש 'חימום' תקופה של שיפור הביצועים - בדומה ל-JVM אחר תהליכים.

אופטימיזציה של תצורת ה-build

הטיפים הבאים יעזרו לך לשפר את ה-build המהירות של פרויקט Android Studio.

עדכון הכלים

הכלים של Android מקבלים אופטימיזציות של build ותכונות חדשות, בכל עדכון. חלק מהטיפים בדף הזה מניחים שאתם משתמשים . כדי ליהנות מהשיפורים האחרונים, כדאי להמשיך להתעדכן עד תאריך:

שימוש ב-KSP במקום ב-kapt

הכלי לעיבוד הערות ב-Kotlin (kapt) איטי באופן משמעותי מה-Kotlin מעבד סמלים (KSP). אם אתם כותבים מקור ב-Kotlin עם הערות ומשתמשים בכלים עיבוד הערות (כמו חדר) שתומך ב-KSP, כדאי לעבור ל-KSP.

נמנעים מהידור של משאבים לא נחוצים

יש להימנע מהידור ומאריזה של משאבים שלא בודקים, כמו משאבים נוספים ללוקליזציה של שפות ומשאבים בדחיסות מסך. במקום זאת, צריך לציין רק משאב שפה ודחיסות מסך עבור ה-"dev" בטעם שלך, כפי שמוצג בדוגמה הבאה:

מגניב

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

Kotlin

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() מאגרים. עם זאת, ייתכן שה-build שלך צריכים יישומי פלאגין של צד שלישי שנפתרים באמצעות gradlePluginPortal() לאחר השיפור.

Gradle מחפשת מאגרים לפי הסדר שבו הם הוצהרו, כך שביצועי ה-build ישתפרו אם המאגרים המפורטים קודם מכילים רוב יישומי הפלאגין. לכן, כדאי לנסות את gradlePluginPortal() באמצעות הכנסתה האחרונה בבלוק המאגר ב-settings.gradle חדש. ברוב המקרים, הפעולה הזו מפחיתה את מספר החיפושים המיותרים של יישומי פלאגין משפר את מהירות ה-build.

למידע נוסף על האופן שבו Gradle מנווטת בין מאגרים מרובים, ראו הצהרה על כמה מאגרים במסמכי התיעוד של Gradle.

שימוש בערכים של הגדרות build סטטיות ב-build של ניפוי באגים

השתמשו תמיד בערכים סטטיים למאפיינים שנכללים בקובץ המניפסט או בקובצי המשאבים של סוג build של ניפוי באגים.

באמצעות קודים דינמיים של גרסאות, שמות גרסאות, משאבים או כל לוגיקת build אחרת שמשנה את קובץ המניפסט מחייבת גרסת build מלאה של האפליקציה בכל פעם שרוצים להריץ שינוי, גם אם השינוי בפועל עשוי אחרת נדרשת רק החלפה חמה. אם תצורת ה-build שלך דורשת נכסים דינמיים, ולאחר מכן מבודדים את המאפיינים האלה לווריאציות של גרסת ה-build של הגרסה הערכים הסטטיים של גרסאות ה-build של ניפוי הבאגים, כפי שמוצג בדוגמה הבאה:

  ...
  // 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.+'). שימוש במספרי גרסאות דינמיים עלול לגרום לעדכוני גרסאות לא צפויים, וקשה לפתור את הבעיה בגרסה הבדלים, וגרסאות build איטיות יותר שנגרמות עקב בדיקת העדכונים ב-Gradle. במקום זאת, יש להשתמש במספרי גרסה סטטיים.

יצירת מודולים של ספרייה

מחפשים באפליקציה קוד שאפשר להמיר למודול של ספריית Android. מודולציה של הקוד בדרך הזו מאפשרת למערכת ה-build להדר רק את הקוד שתשנו את הפלט ושתשמרו במטמון של גרסאות build עתידיות. מודולריזציה גם ביצוע פרויקטים במקביל יעיל יותר לאפשר את האופטימיזציה הזו.

יצירת משימות ללוגיקת build בהתאמה אישית

אחרי שיוצרים פרופיל build, אם גרסת ה-build נראה שחלק ארוך יחסית מזמן ה-build מושקע ב **הגדרת שלב הפרויקטים**, בודקים את הסקריפטים build.gradle ומחפשים את הקוד שאפשר לכלול במשימה מותאמת אישית של Gradle. על ידי הזזה של לוגיקת build במשימה, אתם עוזרים לוודא שהמשימה פועלת רק במקרה הצורך, ניתן לשמור תוצאות במטמון גרסאות build נוספות, ולוגיקת ה-build הזו תהיה כשירה לפעול במקביל אם מפעילים ביצוע פרויקטים במקביל. מידע נוסף על taks ל-build בהתאמה אישית של הלוגיקה, קראו את התיעוד הרשמי של Gradle.

טיפ: אם ה-build כולל מספר גדול של משימות בהתאמה אישית, רוצה לארגן את קובצי build.gradle שלך על ידי יצירת מחלקות משימות בהתאמה אישית. להוסיף את הכיתות שלכם ספריית project-root/buildSrc/src/main/groovy/; Gradle כוללת את הכיתות האלה בנתיב הכיתה באופן אוטומטי לכולם build.gradle קבצים בפרויקט שלך.

המרת תמונות ל-WebP

WebP הוא קובץ תמונה פורמט שמספק דחיסת נתונים מסוג Lossy (כמו JPEG) וגם שקיפות (למשל PNG). דחיסת נתונים טובה יותר ב-WebP מאשר בפורמט JPEG או PNG.

הקטנת הגודל של קובצי התמונה ללא צורך בדחיסת נתונים בזמן ה-build יכולה להאיץ את גרסאות ה-build שלך, במיוחד אם האפליקציה משתמשת בהרבה קובצי תמונה במשאבי אנוש. אבל יכול להיות שתהיה עלייה קטנה בשימוש במעבד (CPU) של המכשיר שמבטלת דחיסה של תמונות WebP. משתמשים ב-Android Studio בקלות המרת התמונות ל-WebP.

השבתת דחיסת קובצי PNG

אם לא ממירים את קובץ ה-PNG של קובצי אימג' ל-WebP, עדיין אפשר להאיץ את ה-build על ידי השבתה של האפשרות לדחוס את התמונה בכל פעם שאתם יוצרים את האפליקציה.

אם משתמשים בפלאגין של Android Gradle גרסה 3.0.0 כוונון של קובצי PNG מושבת כברירת מחדל עבור 'ניפוי באגים' סוג ה-build. כדי להשבית את זה אופטימיזציה של סוגי build אחרים, יש להוסיף את הפרטים הבאים לקובץ build.gradle:

מגניב

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

Kotlin

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

מכיוון שסוגי ה-build או טעמי המוצרים לא מגדירים את הנכס הזה, צריך להגדיר את המאפיין הזה באופן ידני כ-true כשיוצרים את הגרסה של האפליקציה.

ניסוי של אוסף האשפה המקביל של JVM

אפשר לשפר את הביצועים על ידי הגדרה של אוסף האשפה האופטימלי ל-JVM ב-Gradle. מודל JDK 8 מוגדר להשתמש בכל אוסף אשפה מקביל כברירת מחדל, אבל JDK 9 ואילך מוגדר לשימוש אוסף האשפה של G1.

כדי לשפר את הביצועים של ה-build, אנחנו ממליצים לבדוק את גרסאות ה-build של Gradle אוסף אשפה. ב-gradle.properties, מגדירים את הדברים הבאים:

org.gradle.jvmargs=-XX:+UseParallelGC

אם כבר הוגדרו אפשרויות אחרות בשדה הזה, מוסיפים אפשרות חדשה:

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

כדי למדוד את מהירות ה-build בהגדרות שונות: יצירת פרופיל ה-build.

הגדלת גודל הערימה של JVM

אם תבחינו בבנייה איטית, ובפרט, איסוף האשפה לוקח יותר מ-15% בזמן כלי לניתוח נתונים לכן צריך להגדיל את גודל הערימה (heap) של Java Virtual Machine (JVM). בקובץ gradle.properties, מגדירים מגבלת אחסון של 4, 6 או 8 ג'יגה בייט כפי שאפשר לראות בדוגמה הבאה:

org.gradle.jvmargs=-Xmx6g

לאחר מכן מומלץ לבדוק את מהירות ה-build כדי לשפר אותה. הדרך הקלה ביותר לקבוע את ערך הערימה האופטימלי הוא להגדיל את המגבלה בכמות קטנה ולאחר מכן לבדוק אם יש מספיק build מהירות גבוהה יותר.

אם משתמשים גם אוסף אשפה מקביל של JVM, כל השורה אמורה להיראות כך:

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

כדי לנתח את שגיאות הזיכרון של JVM, מפעילים את הפרמטר HeapDumpOnOutOfMemoryError לסמן מופעל. כך ה-JVM תיצור תמונת מצב של הזיכרון כשייגמר הזיכרון.

שימוש במחלקות R שלא עוברות

משתמשים בכיתות R שלא כדאי לעבור כדי ליצור גרסאות build מהר יותר לאפליקציות עם מספר מודולים. כך ניתן למנוע כפילויות של משאבים על ידי וידוא המחלקה R של כל מודול מכילה רק הפניות למשאבים שלו, ללא שליפת הפניות מ של יחסי התלות שלו. כך אפשר ליצור גרסאות build מהירות יותר וליהנות מהיתרונות של הידור הימנעות. זוהי התנהגות ברירת המחדל בפלאגין Android Gradle מגרסה 8.0.0 ואילך.

החל מ-Android Studio Bumblebee, כיתות R לא-זמניות מופעלות כברירת מחדל בפרויקטים חדשים. בפרויקטים שנוצרו בגרסאות קודמות של Android Studio, צריך לעדכן אותם כך שישתמשו בגרסה לא מעבר R כיתות באמצעות מעבר אל Refactoring > העברה למחלקות R לא-מעבר.

למידע נוסף על מקורות מידע באפליקציות ועל הכיתה R: סקירה כללית של משאבי האפליקציה.

שימוש במחלקות R לא קבועות

שימוש במחלקה R לא קבועה בשדות באפליקציות ובבדיקות לשיפור ההמרות המצטברות של הידור Java ומאפשרת כיווץ משאבים מדויק יותר. R שדות כיתה לא תמיד קבועים לספריות, כי המשאבים ממוספרים כשאריזה את ה-APK של האפליקציה או בדיקה שתלויה בספרייה הזו. זוהי התנהגות ברירת המחדל ב-Android Gradle Plugin מגרסה 8.0.0 ואילך.

השבתת הדגל של Jetifier

מאחר שרוב הפרויקטים משתמשים בספריות של AndroidX באופן ישיר, אפשר להסיר את סימון Jetifier לשיפור ביצועי ה-build. כדי להסיר: דגל Jetifier, מגדירים android.enableJetifier=false קובץ gradle.properties.

ה-Build Analyzer יכול לבצע בדיקה כדי לראות אם הדגל יכול יוסרו באופן בטוח כדי לאפשר לפרויקט לקבל ביצועי build טובים יותר ולהתרחק ספריות תמיכה של Android שאינן מתוחזקות. למידע נוסף על Build Analyzer, פתרון בעיות בביצועים של build

שימוש במטמון של ההגדרות

מטמון ההגדרות מאפשר ל-Gradle לתעד מידע על תרשים משימות ה-build ולעשות בו שימוש חוזר בגרסאות build נוספות, כדי Gradle לא צריכה להגדיר מחדש את כל ה-build.

כדי להפעיל את המטמון של ההגדרות האישיות, צריך לבצע את השלבים הבאים:

  1. בודקים שכל יישומי הפלאגין בפרויקט תואמים.

    משתמשים ב Build Analyzer כדי לבדוק אם הפרויקט תואם למטמון של ההגדרות האישיות. Build Analyzer מריץ רצף של בדיקות build כדי לקבוע אם אפשר להפעיל את התכונה בפרויקט. צפייה בעיה מס' 13490 עבור רשימת יישומי פלאגין נתמכים.

  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

כשהמטמון של ההגדרות מופעל, בפעם הראשונה שתפעילו את הפרויקט, פלט ה-build יופק אומר Calculating task graph as no configuration cache is available for tasks. במהלך בהפעלות הבאות, פלט ה-build יהיה Reusing configuration cache.

למידע נוסף על המטמון של ההגדרות, אפשר לעיין בפוסט בבלוג סקירה מפורטת של שמירת הגדרות במטמון את התיעוד של Gradle על מטמון ההגדרות האישיות.

בעיות במטמון ההגדרה שהוצגו ב-Gradle 8.1 ובפלאגין של Android Gradle גרסה 8.1

מטמון ההגדרות האישיות הפך ליציב ב-Gradle 8.1, ונוספו API של קבצים מעקב. שיחות כמו File.exists(), File.isDirectory() ו-File.list() מוקלטות על ידי Gradle למעקב אחר קובצי קלט של תצורה.

גרסת Android Gradle Plugin (AGP) 8.1 משתמשת בממשקי ה-API האלה של File לחלק מהקבצים ש-Gradle צריך לא מחשיבים כערכי קלט מהמטמון. הפעולה הזו תגרום לביטול תוקף של מטמון נוסף כשמשתמשים בו עם Gradle מגרסה 8.1 ואילך, מאטה את ביצועי ה-build. הרכיבים הבאים נחשבים כקלט של מטמון ב-AGP 8.1:

קלט מעקב אחר בעיות קבוע ב-
$GRADLE_USER_HOME/android/FakeDependency.jar גיליון מס' 289232054 AGP 8.2
פלט cmake גיליון מס' 287676077 AGP 8.2
$GRADLE_USER_HOME/.android/analytics.settings גיליון מס' 278767328 AGP 8.3

אם אתם משתמשים בממשקי ה-API האלה או בפלאגין שמשתמש בממשקי ה-API האלה, יכול להיות שבזמן ה-build תהיה רגרסיה, כי יש לוגיקה של תהליך build באמצעות ממשקי ה-API האלה. עלול לגרום לביטול תוקף נוסף של המטמון. כדאי לעיין שיפורים במעקב אחר הקלט של תצורת build דיון על הדפוסים האלה ואיך לתקן את לוגיקת ה-build, או להשבית באופן זמני למעקב אחרי API של קבצים.