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

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

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

מילון מונחים

Java Development Kit (JDK)
ערכת הפיתוח של Java‏ (JDK) מכילה:
  • כלים כמו קומפיילר, פרופיילר ויוצר ארכיון. הם משמשים מאחורי הקלעים במהלך ה-build כדי ליצור את האפליקציה.
  • ספריות שמכילות ממשקי API שאפשר להפעיל מקוד המקור ב-Kotlin או ב-Java. חשוב לזכור שלא כל הפונקציות זמינות ב-Android.
  • המכונה הווירטואלית של Java‏ (JVM), מפענח שמריץ אפליקציות Java. אתם משתמשים ב-JVM כדי להפעיל את Android Studio IDE ואת כלי ה-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 מחפשים 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 בפועל. אפשר להשתמש בתהליך הזה שוב, כל עוד ה-builds משתמשים באותה גרסה של JDK ו-Gradle. שימוש חוזר ב-daemon מקצר את הזמן שנדרש כדי להפעיל JVM חדש ולאתחל את מערכת ה-build.

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

הגדרת Gradle JDK ב-Android Studio

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

  • פקודות מאקרו כמו JAVA_HOME ו-GRADLE_LOCAL_JAVA_HOME
  • רשומות בטבלת JDK בפורמט vendor-version כמו jbr-17 שמאוחסנות בקובצי התצורה של Android
  • הורדת JDK
  • הוספת JDK ספציפי
  • ערכות JDK שזוהו באופן מקומי מספריית ההתקנה של ערכת ה-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 ולפתרון הפניות ל-API של JDK כשעורכים את הסקריפטים של ה-build ואת קוד המקור. שימו לב שהערך של compileSdk יגביל עוד יותר את סמלי Java שיהיו זמינים כשעורכים ויוצרים את קוד המקור.

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

לדוגמה, גרסה 8.x של הפלאגין של Android Gradle דורשת את JDK 17. אם תנסו להריץ build של Gradle שמשתמש בגרסה מוקדמת יותר של ה-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`.

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

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

Kotlin

android {
    ...
    compileSdk = 36
}

מגניב

android {
    ...
    compileSdk 36
}

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

אתם יכולים להיעזר בטבלה הזו כדי לראות באיזו גרסת 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?

‫Java toolchain JDK מכיל את המקמפל של Java שמשמש לקימפול של כל קוד מקור ב-Java. ה-JDK הזה מריץ גם javadoc ובדיקות יחידה (unit tests) במהלך ה-build.

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

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

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

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

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

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

Kotlin

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

מגניב

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

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

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

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

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

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

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
    }
}

מגניב

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
    }
}

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

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

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

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

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

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

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
        targetCompatibility = JavaVersion.VERSION_17
    }
}

מגניב

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
        targetCompatibility JavaVersion.VERSION_17
    }
}

אם אתם משתמשים בגרסה של Kotlin שהיא ישנה יותר מ-2.2, אתם צריכים להגדיר גם את kotlinOptions:

Kotlin

android {
    kotlinOptions {
        jvmTarget = "17"
    }
}

מגניב

android {
    kotlinOptions {
        jvmTarget = "17"
    }
}

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