גרסאות Java בגרסאות build של Android

לא משנה אם קוד המקור שלכם כתוב ב-Java, ב-Kotlin או בשתיהן, יש כמה מקומות שבהם אתם צריכים לבחור JDK או גרסה של שפת Java בשביל הבנייה.

סקירה כללית של קשרי JDK ב-Gradle build
איור 1. קשרי JDK ב-build

מילון מונחים

Java Development Kit (JDK)
ערכת הכלים לפיתוח Java‏ (JDK) כוללת:
  • כלים כמו קומפיילר, פרופילר ויוצר ארכיון. הם משמשים מאחורי הקלעים במהלך הבנייה כדי ליצור את האפליקציה.
  • ספריות שמכילות ממשקי API שאפשר להפעיל מקוד המקור של Kotlin או Java. חשוב לזכור שלא כל הפונקציות זמינות ב-Android.
  • ‫Java Virtual Machine‏ (JVM), מתורגמן שמבצע אפליקציות Java. משתמשים ב-JVM כדי להריץ את סביבת הפיתוח המשולבת (IDE) של Android Studio ואת כלי ה-build של Gradle. ‫JVM לא נמצא בשימוש במכשירי Android או באמולטורים.
JetBrains Runtime (JBR)
JetBrains Runtime (JBR) הוא JDK משופר שמופץ עם Android Studio. הוא כולל כמה אופטימיזציות לשימוש ב-Studio ובמוצרים קשורים של JetBrains אבל אפשר להשתמש בו גם להרצת אפליקציות אחרות של Java.

איך בוחרים JDK להרצת Android Studio?

מומלץ להשתמש ב-JBR כדי להריץ את Android Studio. הוא מופעל עם Android Studio ומשמש לבדיקות, והוא כולל שיפורים לשימוש אופטימלי ב-Android Studio. כדי לוודא זאת, אל תגדירו את משתנה הסביבה STUDIO_JDK.

סקריפטים להפעלה של Android Studio מחפשים מכונה וירטואלית של Java (JVM) לפי הסדר הבא:

  1. משתנה הסביבה STUDIO_JDK
  2. הספרייה studio.jdk (בהפצה של Android Studio)
  3. jbr (JetBrains Runtime), בהפצה של Android Studio. מומלץ.
  4. משתנה הסביבה JDK_HOME
  5. משתנה הסביבה JAVA_HOME
  6. קובץ ההפעלה java במשתנה הסביבה PATH

איך בוחרים את ה-JDK שדרכו יופעלו ה-builds של Gradle?

אם מריצים את Gradle באמצעות הלחצנים ב-Android Studio, נעשה שימוש ב-JDK שהוגדר בהגדרות של Android Studio כדי להריץ את Gradle. אם מריצים את Gradle בטרמינל, בתוך Android Studio או מחוצה לו, משתנה הסביבה JAVA_HOME (אם הוא מוגדר) קובע איזו JDK מריצה את סקריפטים של Gradle. אם JAVA_HOME לא מוגדר, המערכת משתמשת בפקודה java במשתנה הסביבה PATH.

כדי לקבל את התוצאות הכי עקביות, צריך לוודא שמשתנה הסביבה JAVA_HOME וההגדרה של Gradle JDK ב-Android Studio מוגדרים לאותו JDK.

כשמריצים את ה-build, ‏ Gradle יוצר תהליך שנקרא daemon כדי לבצע את ה-build בפועל. אפשר להשתמש בתהליך הזה שוב, בתנאי שהגרסאות של JDK ו-Gradle זהות ב-builds. שימוש חוזר בשירות רקע מקצר את הזמן שנדרש להפעלת JVM חדש ולאתחול של מערכת ה-build.

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

הגדרת Gradle JDK ב-Android Studio

כדי לשנות את הגדרת ה-JDK של Gradle בפרויקט הקיים, פותחים את ההגדרות של Gradle דרך File (קובץ) (או Android Studio ב-macOS) > Settings > Build, Execution, Deployment > Build Tools > Gradle (הגדרות > בנייה, הפעלה, פריסה > כלי בנייה > Gradle). בתפריט הנפתח Gradle JDK אפשר לבחור מבין האפשרויות הבאות:

  • פקודות מאקרו כמו JAVA_HOME ו-GRADLE_LOCAL_JAVA_HOME
  • רשומות בטבלת JDK בפורמט vendor-version כמו jbr-17 שמאוחסנות בקובצי ההגדרות של Android
  • הורדת JDK
  • הוספת JDK ספציפי
  • ‫JDKs שזוהו באופן מקומי מתוך ספריית ההתקנה של JDK שמוגדרת כברירת מחדל במערכת ההפעלה

האפשרות שנבחרה מאוחסנת באפשרות gradleJvm בקובץ .idea/gradle.xml של הפרויקט, והנתיב שלה ל-JDK משמש להפעלת Gradle כשמתחילים דרך Android Studio.

איור 2. הגדרות Gradle JDK ב-Android Studio.

הפקודות המאקרו מאפשרות בחירה דינמית של נתיב JDK של הפרויקט:

  • JAVA_HOME: משתמש במשתנה הסביבה עם אותו שם
  • GRADLE_LOCAL_JAVA_HOME: משתמש במאפיין java.home בקובץ .gradle/config.properties, שערך ברירת המחדל שלו הוא JetBrains Runtime.

ה-JDK שנבחר משמש להרצת ה-build של Gradle ולפתרון הפניות ל-JDK API כשעורכים את סקריפטים ה-build ואת קוד המקור. שימו לב: הפרמטר compileSdk שצוין יגביל עוד יותר את הסמלים של Java שיהיו זמינים כשעורכים ובונים את קוד המקור.

חשוב לבחור גרסת JDK שהיא לפחות כמו גרסאות ה-JDK שמשמשות את הפלאגינים שבהם אתם משתמשים ב-Gradle build. כדי לדעת מהי גרסת ה-JDK המינימלית הנדרשת עבור Android Gradle Plugin ‏ (AGP), אפשר לעיין בטבלת התאימות בהערות על הגרסה.

לדוגמה, גרסה 8.x של Android Gradle Plugin דורשת JDK 17. אם תנסו להריץ Gradle build שמשתמש בגרסה מוקדמת יותר של JDK, תוצג הודעה כמו:

An exception occurred applying plugin request [id: 'com.android.application']
> Failed to apply plugin 'com.android.internal.application'.
   > Android Gradle plugin requires Java 17 to run. You are currently using Java 11.
      Your current JDK is located in /usr/local/buildtools/java/jdk
      You can try some of the following options:
       - changing the IDE settings.
       - changing the JAVA_HOME environment variable.
       - changing `org.gradle.java.home` in `gradle.properties`.

באילו ממשקי Java API אפשר להשתמש בקוד המקור ב-Java או ב-Kotlin?

אפליקציית Android יכולה להשתמש בחלק מממשקי ה-API שמוגדרים ב-JDK, אבל לא בכולם. ערכת ה-SDK של Android מגדירה הטמעות של פונקציות רבות בספריית Java כחלק מממשקי ה-API הזמינים שלה. המאפיין compileSdk מציין באיזו גרסת Android SDK להשתמש כשמדרים את קוד המקור של Kotlin או Java.

Kotlin

android {
    ...
    compileSdk = 36
}

Groovy

android {
    ...
    compileSdk 36
}

כל גרסה של Android תומכת בגרסה ספציפית של JDK ובקבוצת משנה של ממשקי Java API שזמינים בה. אם אתם משתמשים ב-Java API שזמין ב-compileSdk שלא זמין ב-minSdk שצוין, יכול להיות שתוכלו להשתמש ב-API בגרסה קודמת של Android באמצעות תהליך שנקרא desugaring. כאן אפשר לראות אילו ממשקי API נתמכים ב-Java 11 ומעלה באמצעות desugaring.

בטבלה הזו אפשר לראות איזו גרסת Java נתמכת בכל Android API, ואיפה אפשר למצוא פרטים על ממשקי Java API שזמינים.

Android Java תכונות שנתמכות ב-API ובשפה
‫14 (API 34) 17 ספריות ליבה
‫13 (API 33) 11 ספריות ליבה
‫12 (API 32) 11 Java API
‫11 ומטה גרסאות Android

איזה JDK קומפל את קוד המקור של Java?

‫JDK של Java toolchain מכיל את מהדר Java שמשמש להידור של כל קוד המקור של Java. בנוסף, ה-JDK הזה מריץ javadoc ובדיקות יחידה במהלך ה-build.

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

כדי ליצור גרסה עקבית יותר, אפשר לציין במפורש גרסה של Java Toolchain. הגדרת הערך הזה:

  • הכלי מאתר JDK תואם במערכת שבה מופעל ה-build.
    • אם לא קיימת JDK תואמת (ומוגדר כלי לפתרון בעיות בשרשרת הכלים), המערכת תוריד אחת.
  • חושף את ממשקי ה-API של Java בשרשרת הכלים לקריאות מקוד המקור.
  • הוא יוצר קובץ הפעלה מקוד המקור של Java באמצעות גרסת שפת Java.
  • הערכים שמוגדרים כברירת מחדל הם sourceCompatibility ו-targetCompatibility.

מומלץ תמיד לציין את ערכת הכלים של Java, ולוודא שערכת ה-JDK שצוינה מותקנת, או להוסיף toolchain resolver ל-build.

אתם יכולים לציין את ערכת הכלים בין אם קוד המקור שלכם כתוב ב-Java, ב-Kotlin או בשניהם. מציינים את שרשרת הכלים ברמה העליונה של קובץ build.gradle(.kts) של המודול.

מציינים את הגרסה של Java toolchain באופן הבא:

Kotlin

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

Groovy

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(17)
    }
}

האפשרות הזו פועלת אם המקור הוא Kotlin,‏ Java או שילוב של שתי השפות.

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

באילו תכונות של שפת Java אפשר להשתמש בקוד המקור של Java?

המאפיין sourceCompatibility קובע אילו תכונות של שפת Java זמינות במהלך קומפילציה של מקור Java. היא לא משפיעה על מקור Kotlin.

מציינים sourceCompatibility בקובץ build.gradle(.kts) של המודול באופן הבא:

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
    }
}

Groovy

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
    }
}

אם לא מציינים ערך למאפיין הזה, ברירת המחדל היא הגרסה של Java toolchain. אם אתם לא משתמשים ב-Java toolchain, ברירת המחדל היא גרסה שנבחרה על ידי Android Gradle plugin (לדוגמה, Java 8 ואילך).

באילו תכונות של קובץ בינארי של Java אפשר להשתמש כשמהדרים את קוד המקור של Kotlin או Java?

המאפיינים targetCompatibility ו-jvmTarget קובעים את גרסת פורמט המחלקה של Java שבה נעשה שימוש כשיוצרים קוד בייט עבור מקורות של Java ו-Kotlin שעברו קומפילציה, בהתאמה.

חלק מהתכונות של Kotlin היו קיימות לפני שהוספו תכונות מקבילות ב-Java. קומפיילרים מוקדמים של Kotlin נאלצו ליצור דרך משלהם לייצוג התכונות האלה של Kotlin. חלק מהתכונות האלה נוספו מאוחר יותר ל-Java. בגרסאות מאוחרות יותר של jvmTarget, יכול להיות שהקומפיילר של Kotlin ישתמש ישירות בתכונה של Java, מה שעשוי להוביל לביצועים טובים יותר.

גרסאות שונות של Android תומכות בגרסאות שונות של Java. כדי להשתמש בתכונות נוספות של Java, אפשר להגדיל את targetCompatibility ואת jvmTarget, אבל יכול להיות שתצטרכו גם להגדיל את גרסת ה-SDK המינימלית של Android כדי לוודא שהתכונה זמינה.

שימו לב שהערך של targetCompatibility חייב להיות גדול מהערך של sourceCompatibility או שווה לו. בפועל, בדרך כלל צריך להשתמש באותו ערך בשדות sourceCompatibility,‏ targetCompatibility ו-jvmTarget. אפשר להגדיר אותם כך:

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget = "17"
    }
}

Groovy

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
    kotlinOptions {
        jvmTarget '17'
    }
}

אם לא מציינים את המאפיינים האלה, ברירת המחדל היא הגרסה של Java toolchain. אם אתם לא משתמשים בשרשרת כלים של Java, יכול להיות שערכי ברירת המחדל יהיו שונים ויובילו לבעיות בבנייה. לכן, מומלץ תמיד לציין את הערכים האלה באופן מפורש או להשתמש בערכת כלים של Java.