Phiên bản Java trong các bản dựng Android

Dù mã nguồn được viết bằng Java, Kotlin hay cả hai, có một số nơi bạn phải chọn phiên bản ngôn ngữ JDK hoặc Java cho bản dựng.

Tổng quan về mối quan hệ JDK trong bản dựng Gradle
Hình 1. Mối quan hệ JDK trong một bản dựng

Bảng chú giải thuật ngữ

Bộ phát triển Java (JDK)
Bộ phát triển Java (JDK) chứa:
  • Các công cụ, chẳng hạn như trình biên dịch, trình phân tích tài nguyên và trình tạo bản lưu trữ. Các tệp này được sử dụng trong quá trình xây dựng để tạo ứng dụng.
  • Thư viện chứa các API mà bạn có thể gọi từ mã nguồn Kotlin hoặc Java. Xin lưu ý rằng không phải hàm nào cũng có trên Android.
  • Máy ảo Java (JVM), một trình thông dịch thực thi các ứng dụng Java. Bạn sử dụng JVM để chạy IDE Android Studio và công cụ bản dựng Gradle. JVM không được dùng trên các thiết bị Android hoặc trình mô phỏng.
JetBrains Runtime (JBR)
JetBrains Runtime (JBR) là một JDK nâng cao, được phân phối cùng với Android Studio. Công cụ này bao gồm một số tính năng tối ưu hoá để sử dụng trong Studio và các sản phẩm JetBrains liên quan, nhưng cũng có thể dùng để chạy các ứng dụng Java khác.

Làm cách nào để chọn JDK để chạy Android Studio?

Bạn nên sử dụng JBR để chạy Android Studio. Thư viện này được triển khai và dùng để kiểm thử Android Studio, đồng thời bao gồm các tính năng nâng cao để sử dụng Android Studio một cách tối ưu. Để đảm bảo điều này, đừng đặt biến môi trường STUDIO_JDK.

Tập lệnh khởi động cho Android Studio tìm kiếm JVM theo thứ tự sau:

  1. Biến môi trường STUDIO_JDK
  2. Thư mục studio.jdk (trong bản phân phối Android Studio)
  3. Thư mục jbr (JetBrains Runtime), trong bản phân phối Android Studio. Được đề xuất.
  4. Biến môi trường JDK_HOME
  5. Biến môi trường JAVA_HOME
  6. java có thể thực thi trong biến môi trường PATH

Làm cách nào để chọn JDK chạy bản dựng Gradle?

Nếu bạn chạy Gradle bằng các nút trong Android Studio, thì JDK được đặt trong phần cài đặt Android Studio sẽ được dùng để chạy Gradle. Nếu bạn chạy Gradle trong một thiết bị đầu cuối, bên trong hoặc bên ngoài Android Studio, thì biến môi trường JAVA_HOME (nếu được đặt) sẽ xác định JDK nào chạy các tập lệnh Gradle. Nếu bạn không đặt JAVA_HOME, thì lệnh này sẽ sử dụng lệnh java trên biến môi trường PATH.

Để có kết quả nhất quán nhất, hãy đảm bảo bạn đặt biến môi trường JAVA_HOMEcấu hình JDK của Gradle trong Android Studio thành cùng một JDK.

Khi chạy bản dựng, Gradle sẽ tạo một quy trình có tên là trình nền để thực hiện bản dựng thực tế. Bạn có thể sử dụng lại quy trình này, miễn là các bản dựng đang sử dụng cùng một phiên bản JDK và Gradle. Việc sử dụng lại trình nền sẽ giảm thời gian khởi động JVM mới và khởi chạy hệ thống xây dựng.

Nếu bạn bắt đầu các bản dựng bằng nhiều phiên bản JDK hoặc Gradle, các trình nền bổ sung sẽ được tạo, tiêu tốn nhiều CPU và bộ nhớ hơn.

Cấu hình JDK của Gradle trong Android Studio

Để sửa đổi cấu hình JDK Gradle của dự án hiện có, hãy mở phần cài đặt Gradle trong mục File (Tệp) (hoặc Android Studio trên macOS) > Settings > Build, Execution, Deployment > Build Tools > Gradle (Tệp > Cài đặt > Xây dựng, Thực thi, Triển khai > Công cụ xây dựng > Gradle). Trình đơn thả xuống Gradle JDK chứa các tuỳ chọn sau để bạn chọn:

  • Macro như JAVA_HOMEGRADLE_LOCAL_JAVA_HOME
  • Mục nhập bảng JDK ở định dạng vendor-version như jbr-17 được lưu trữ trong tệp cấu hình Android
  • Tải JDK xuống
  • Thêm một JDK cụ thể
  • JDK được phát hiện cục bộ từ thư mục cài đặt JDK mặc định của hệ điều hành

Tuỳ chọn đã chọn được lưu trữ trong tuỳ chọn gradleJvm trong tệp .idea/gradle.xml của dự án và độ phân giải đường dẫn JDK được dùng để chạy Gradle khi khởi động thông qua Android Studio.

Hình 2. Chế độ cài đặt JDK của Gradle trong Android Studio.

Các macro này cho phép lựa chọn đường dẫn JDK của dự án một cách linh động:

  • JAVA_HOME: sử dụng biến môi trường có cùng tên
  • GRADLE_LOCAL_JAVA_HOME: sử dụng thuộc tính java.home trong tệp .gradle/config.properties, mặc định là Thời gian chạy JetBrains.

JDK đã chọn được dùng để chạy bản dựng Gradle và phân giải các tệp tham chiếu API JDK khi chỉnh sửa tập lệnh bản dựng và mã nguồn. Lưu ý rằng compileSdk được chỉ định sẽ hạn chế hơn nữa các biểu tượng Java sẽ có sẵn khi chỉnh sửa và tạo mã nguồn.

Hãy nhớ chọn phiên bản JDK cao hơn hoặc bằng phiên bản JDK mà các trình bổ trợ mà bạn sử dụng trong bản dựng Gradle sử dụng. Để xác định phiên bản JDK tối thiểu bắt buộc cho Trình bổ trợ Android cho Gradle (AGP), hãy xem bảng tương thích trong ghi chú phát hành.

Ví dụ: Trình bổ trợ Android cho Gradle phiên bản 8.x yêu cầu JDK 17. Nếu bạn cố chạy bản dựng Gradle sử dụng với phiên bản JDK cũ hơn, thì công cụ này sẽ báo cáo một thông báo như sau:

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`.

Tôi có thể sử dụng API Java nào trong mã nguồn Java hoặc Kotlin?

Một ứng dụng Android có thể sử dụng một số API được xác định trong JDK, nhưng không phải tất cả. SDK Android xác định việc triển khai nhiều hàm thư viện Java trong các API hiện có. Thuộc tính compileSdk chỉ định phiên bản SDK Android cần sử dụng khi biên dịch mã nguồn Kotlin hoặc Java.

Kotlin

android {
    ...
    compileSdk = 33
}

Groovy

android {
    ...
    compileSdk 33
}

Mỗi phiên bản Android hỗ trợ một phiên bản JDK cụ thể và một tập hợp con các API Java hiện có. Nếu sử dụng một API Java có trong compileSdk không có trong minSdk đã chỉ định, thì bạn có thể sử dụng API đó trong phiên bản Android cũ thông qua một quy trình gọi là đơn giản hoá. Hãy xem phần Các API Java 11 trở lên hiện có thông qua quy trình đơn giản hoá để biết các API được hỗ trợ.

Sử dụng bảng này để xác định phiên bản Java mà mỗi API Android hỗ trợ và nơi tìm thông tin chi tiết về các API Java có sẵn.

Android Java Các tính năng về API và ngôn ngữ được hỗ trợ
14 (API 34) 17 Thư viện chính
13 (API 33) 11 Thư viện chính
12 (API 32) 11 API Java
11 trở xuống Phiên bản Android

JDK nào biên dịch mã nguồn Java của tôi?

JDK chuỗi công cụ Java chứa trình biên dịch Java dùng để biên dịch mọi mã nguồn Java. JDK này cũng chạy javadoc và kiểm thử đơn vị trong quá trình xây dựng.

Chuỗi công cụ mặc định là JDK dùng để chạy Gradle. Nếu bạn sử dụng mặc định và chạy một bản dựng trên nhiều máy (ví dụ: máy cục bộ và máy chủ Tích hợp liên tục riêng biệt), kết quả của bản dựng có thể khác nhau nếu bạn sử dụng nhiều phiên bản JDK.

Để tạo một bản dựng nhất quán hơn, bạn có thể chỉ định rõ ràng phiên bản chuỗi công cụ Java. Chỉ định điều này:

  • Tìm một JDK tương thích trên hệ thống đang chạy bản dựng.
    • Nếu không có JDK tương thích (và trình phân giải chuỗi công cụ được xác định), hãy tải một JDK xuống.
  • Hiển thị các API Java của chuỗi công cụ cho các lệnh gọi từ mã nguồn.
  • Biên dịch nguồn Java bằng phiên bản ngôn ngữ Java.
  • Cung cấp giá trị mặc định cho sourceCompatibilitytargetCompatibility.

Bạn nên luôn chỉ định chuỗi công cụ Java và đảm bảo rằng JDK đã chỉ định được cài đặt hoặc thêm trình phân giải chuỗi công cụ vào bản dựng.

Bạn có thể chỉ định chuỗi công cụ xem mã nguồn được viết bằng Java, Kotlin hay cả hai. Chỉ định chuỗi công cụ ở cấp cao nhất của tệp build.gradle(.kts) của mô-đun.

Chỉ định phiên bản chuỗi công cụ Java như sau:

Kotlin

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

Groovy

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

Cách này hiệu quả nếu nguồn của bạn là Kotlin, Java hoặc kết hợp cả hai.

Phiên bản JDK của chuỗi công cụ có thể giống với phiên bản JDK dùng để chạy Gradle, nhưng hãy lưu ý rằng chúng phục vụ các mục đích khác nhau.

Tôi có thể sử dụng tính năng nguồn ngôn ngữ Java nào trong mã nguồn Java?

Thuộc tính sourceCompatibility xác định các tính năng ngôn ngữ Java có sẵn trong quá trình biên dịch nguồn Java. Việc này không ảnh hưởng đến nguồn Kotlin.

Chỉ định sourceCompatibility trong tệp build.gradle(.kts) của mô-đun như sau:

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_17
    }
}

Groovy

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_17
    }
}

Nếu không được chỉ định, thuộc tính này sẽ là phiên bản chuỗi công cụ Java theo mặc định. Nếu bạn không sử dụng chuỗi công cụ Java, thì chuỗi công cụ này sẽ mặc định là phiên bản do trình bổ trợ Android cho Gradle chọn (ví dụ: Java 8 trở lên).

Tôi có thể sử dụng tính năng tệp nhị phân Java nào khi biên dịch nguồn Kotlin hoặc Java?

Thuộc tính targetCompatibilityjvmTarget xác định phiên bản định dạng lớp Java được dùng khi tạo mã byte cho nguồn Java và Kotlin đã biên dịch tương ứng.

Một số tính năng của Kotlin đã tồn tại trước khi các tính năng tương đương của Java được thêm vào. Các trình biên dịch Kotlin ban đầu phải tạo cách riêng để biểu thị các tính năng Kotlin đó. Sau đó, một số tính năng này đã được thêm vào Java. Với các cấp jvmTarget sau này, trình biên dịch Kotlin có thể trực tiếp sử dụng tính năng Java, nhờ đó có thể mang lại hiệu suất tốt hơn.

Các phiên bản Android khác nhau hỗ trợ các phiên bản Java khác nhau. Bạn có thể tận dụng các tính năng bổ sung của Java bằng cách tăng targetCompatibilityjvmTarget, nhưng cũng có thể buộc bạn phải tăng phiên bản SDK Android tối thiểu để đảm bảo tính năng này có sẵn.

Lưu ý rằng targetCompatibility phải lớn hơn hoặc bằng sourceCompatibility. Trong thực tế, sourceCompatibility, targetCompatibilityjvmTarget thường sử dụng cùng một giá trị. Bạn có thể thiết lập như sau:

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'
    }
}

Nếu không được chỉ định, các thuộc tính này sẽ được đặt mặc định thành phiên bản chuỗi công cụ Java. Nếu bạn không sử dụng chuỗi công cụ Java, thì các giá trị mặc định có thể khác nhau và gây ra sự cố bản dựng. Do đó, bạn nên luôn chỉ định rõ các giá trị này hoặc sử dụng chuỗi công cụ Java.