d8

d8 là một công cụ dòng lệnh được Android Studio và Trình bổ trợ Android cho Gradle sử dụng để biên dịch mã byte Java của dự án thành mã byte DEX chạy trên các thiết bị Android, đồng thời cho phép bạn sử dụng các tính năng ngôn ngữ Java 8 trong mã ứng dụng.

d8 cũng được đưa vào dưới dạng một công cụ độc lập trong Công cụ xây dựng Android 28.0.1 trở lên: android_sdk/build-tools/version/.

Cách sử dụng chung

Sử dụng d8 rất đơn giản, chỉ yêu cầu một đường dẫn tới mã byte Java được biên dịch, là mã bạn muốn chuyển đổi thành mã byte DEX, như thể hiện bên dưới.

d8 MyProject/app/build/intermediates/classes/debug/*/*.class

Mã byte đầu vào có thể là sự kết hợp bất kỳ của các tệp hoặc vùng chứa *.class, chẳng hạn như tệp JAR, APK hoặc ZIP. Bạn cũng có thể đưa tệp DEX vào d8 để hợp nhất các tệp đó vào kết quả đầu ra DEX. Điều này rất hữu ích khi gộp một bản dựng tăng dần (incremental build) trong kết quả đầu ra.

Theo mặc định, d8 sẽ biên dịch mã byte Java thành các tệp DEX được tối ưu hoá và chứa thông tin gỡ lỗi, phục vụ cho quá trình gỡ lỗi mã trong thời gian chạy. Tuy nhiên, bạn có thể bao gồm các cờ tuỳ chọn để, ví dụ: tạo bản dựng tăng dần, chỉ định các lớp cần được biên dịch trong tệp DEX chính và chỉ định đường dẫn đến các tài nguyên bổ sung cần thiết để sử dụng các tính năng của ngôn ngữ Java 8.

d8 path-to-input-files [options]

Bảng sau đây mô tả các cờ tuỳ chọn bạn có thể sử dụng với d8.

Tuỳ chọn Mô tả
--debug Biên dịch mã byte DEX để bao gồm thông tin gỡ lỗi, chẳng hạn như bảng ký hiệu gỡ lỗi.

Tuỳ chọn này được bật theo mặc định. Để bao gồm thông tin gỡ lỗi trong mã byte DEX, d8 dự kiến rằng mã byte Java đầu vào sẽ bao gồm thông tin đó. Ví dụ: nếu đang sử dụng javac để biên dịch mã, bạn cần chuyển sang cờ -g này để bao gồm thông tin gỡ lỗi trong mã byte Java đầu ra.

Khi biên dịch tệp DEX cho phiên bản phát hành của ứng dụng hoặc thư viện, hãy sử dụng cờ --release như mô tả bên dưới.

--release Biên dịch mã DEX byte mà không cần thông tin gỡ lỗi. Tuy nhiên, d8 bao gồm một số thông tin được sử dụng khi tạo dấu vết ngăn xếp (stacktrace) và ngoại lệ ghi nhật ký.

Bạn nên chuyển sang cờ này khi biên dịch mã byte cho bản phát hành công khai.

--output path Chỉ định đường dẫn mong muốn cho đầu ra DEX. Theo mặc định, d8 sẽ xuất ra (các) tệp DEX trong thư mục đang hoạt động.

Nếu bạn chỉ định một đường dẫn và tên của tệp ZIP hoặc JAR, d8 sẽ tạo tệp được chỉ định và bao gồm các tệp DEX đầu ra. Nếu bạn chỉ định đường dẫn đến một thư mục hiện có, thì d8 sẽ xuất ra các tệp DEX trong thư mục đó.

--lib android_sdk/platforms/api-level/android.jar Chỉ định đường dẫn đến android.jar của SDK Android. Cờ này là bắt buộc khi biên dịch mã byte có sử dụng các tính năng ngôn ngữ Java 8.
--classpath path Chỉ định tài nguyên đường dẫn lớp (classpath resources) theo yêu cầu của d8 để biên dịch các tệp DEX của dự án. Đặc biệt, d8 sẽ yêu cầu bạn chỉ định một số tài nguyên nhất định khi biên dịch mã byte có sử dụng các tính năng ngôn ngữ Java 8.
--min-api number Chỉ định cấp độ API tối thiểu mà bạn muốn các tệp DEX đầu ra hỗ trợ.
--intermediate Chuyển sang cờ này để d8 biết rằng bạn không biên dịch toàn bộ mã byte Java cho dự án. Cờ này sẽ hữu ích khi tạo bản dựng tăng dần – thay vì biên dịch các tệp DEX được tối ưu hoá bạn dự kiến sẽ chạy trên một thiết bị nào đó, d8 sẽ tạo các tệp DEX trung gian và lưu trữ các tệp đó trong những kết quả đầu ra được chỉ định hoặc đường dẫn mặc định.

Nếu muốn biên dịch tệp DEX được dự định sẽ chạy trên một thiết bị nào đó, bạn hãy xoá cờ này và chỉ định đường dẫn đến lớp DEX trung gian dưới dạng thông tin đầu vào.

--file-per-class Biên dịch mỗi lớp thành các tệp DEX riêng biệt.

Cờ này cho phép bạn tạo nhiều bản dựng tăng dần hơn bằng cách chỉ biên dịch lại các lớp đã thay đổi trước đó. Khi tạo các bản dựng tăng dần bằng Trình bổ trợ Android cho Gradle, tính năng tối ưu hoá này sẽ được bật theo mặc định.

Bạn không thể sử dụng cờ này khi chỉ định đồng thời --main-dex-list.

--no-desugaring Tắt các tính năng ngôn ngữ Java 8. Chỉ dùng cờ này nếu bạn không có ý định biên dịch mã byte Java mà chỉ sử dụng các tính năng ngôn ngữ Java 8.
--main-dex-list path Chỉ định tệp văn bản dùng để liệt kê các lớp d8 nên bao gồm trong tệp DEX chính, thường có tên là classes.dex. Nghĩa là nếu bạn không sử dụng cờ này để chỉ định danh sách các lớp, d8 sẽ không đảm bảo lớp nào sẽ được đưa vào tệp DEX chính.

Do hệ thống Android sẽ tải tệp DEX chính trước khi khởi động ứng dụng, nên bạn có thể sử dụng cờ này để ưu tiên một số lớp nhất định ở thời điểm khởi động bằng cách biên dịch các lớp đó thành tệp DEX chính. Cách này đặc biệt hữu ích khi hỗ trợ các chế độ multidex cũ vì chỉ có các lớp trong tệp DEX chính mới có thể sẵn sàng trong thời gian chạy cho đến khi tải xong thư viện multidex cũ.

Lưu ý rằng mỗi tệp DEX vẫn phải đáp ứng giới hạn tham chiếu 64K. Vì vậy, hãy đảm bảo không chỉ định quá nhiều lớp cho tệp DEX chính, nếu không sẽ gây ra lỗi biên dịch. Theo mặc định, khi chỉ định các lớp bằng --main-dex-list, d8 sẽ bao gồm _only_ các lớp đó trong tệp DEX chính. Điều này sẽ giúp dễ dàng gỡ lỗi các sự cố liên quan đến các lớp bị thiếu trong tệp DEX chính. Nếu chỉ định chế độ --release, d8 sẽ cố gắng giảm số lượng tệp DEX được đóng gói trong phiên bản phát hành của ứng dụng bằng cách đưa càng nhiều lớp khác vào tệp DEX chính càng tốt, cho đến khi đạt giới hạn tham chiếu 64K.

Bạn không thể sử dụng cờ này khi chỉ định đồng thời --file-per-class.

--version In phiên bản d8 bạn đang sử dụng.
--help In văn bản hướng dẫn sử dụng d8

Tạo các bản dựng tăng dần

Để cải thiện tốc độ bản dựng trong quá trình phát triển, chẳng hạn như tích hợp liên tục các bản dựng, bạn có thể hướng dẫn để d8 chỉ biên dịch một tập hợp con của mã byte Java trong dự án. Ví dụ: nếu bật tính năng dexing cho mỗi lớp, bạn chỉ có thể biên dịch lại các lớp đã chỉnh sửa kể từ phiên bản trước đó.

Lệnh sau đây sẽ tạo một bản dựng tăng dần của một vài lớp và bật tính năng dexing cho mỗi lớp. Lệnh này cũng chỉ định một thư mục đầu ra cho bản dựng tăng dần đó.

d8 MainActivity.class R.class --intermediate --file-per-class --output ~/build/intermediate/dex

Khi tạo bản dựng tăng dần, d8 sẽ lưu trữ thêm thông tin trong kết quả đầu ra DEX. Thông tin này sau đó sẽ được dùng để xử lý chính xác tuỳ chọn --main-dex-list và hợp nhất các tệp DEX trong toàn bộ quá trình tạo ứng dụng. Ví dụ: khi xử lý các lớp lambda trên Java 8, d8 sẽ theo dõi lớp lamdba nào được tạo cho mỗi lớp đầu vào. Trong quá trình tạo bản dựng đầy đủ, khi đưa một lớp vào trong tệp DEX chính, d8 sẽ tham khảo siêu dữ liệu để đảm bảo tất cả lớp lambda đã tạo cho lớp đó cũng được đưa vào tệp DEX chính.

Nếu đã biên dịch tất cả mã byte của dự án thành các tệp DEX trên nhiều bản dựng tăng dần, bạn có thể tạo một bản dựng đầy đủ bằng cách truyền thư mục của các tệp DEX trung gian cho d8, như thể hiện dưới đây. Ngoài ra, bạn có thể chỉ định các lớp bạn muốn d8 biên dịch thành tệp DEX chính bằng cách sử dụng --main-dex-list. Vì đầu vào là một tập hợp các tệp đã được biên dịch thành mã byte DEX, bản dựng này sẽ hoàn thành nhanh hơn một bản dựng sạch ngay từ đầu.

d8 ~/build/intermediate/dex --release --main-dex-list ~/build/classes.txt --output ~/build/release/dex

Biên dịch mã byte sử dụng các tính năng ngôn ngữ Java 8

d8 cho phép bạn sử dụng các tính năng ngôn ngữ Java 8 trong mã của mình thông qua một quy trình biên dịch có tên là đơn giản hoá (desugaring). Quy trình này sẽ chuyển đổi các tính năng ngôn ngữ hữu ích này thành mã byte có thể chạy trên nền tảng Android.

Android Studio và Trình bổ trợ Android cho Gradle sẽ thêm các tài nguyên đường dẫn lớp theo yêu cầu của d8 để bật tính năng đơn giản hoá này. Tuy nhiên, khi sử dụng d8 bằng dòng lệnh, bạn phải tự thêm các tài nguyên đó.

Một trong những tài nguyên như vậy là android.jar trong SDK Android mục tiêu. Tài nguyên này bao gồm một nhóm API trên nền tảng Android. Bạn sẽ chỉ định đường dẫn của API đó bằng cách sử dụng cờ --lib.

Một tài nguyên khác là tập hợp mã byte Java đã biên dịch của dự án nhưng hiện tại chưa được biên dịch thành mã byte DEX, được dùng để biên dịch các lớp khác thành mã byte DEX. Ví dụ: nếu mã của bạn sử dụng các phương thức giao diện mặc định và giao diện tĩnh, là một tính năng ngôn ngữ Java 8, bạn cần sử dụng cờ này để chỉ định đường dẫn đến tất cả mã byte Java của dự án, ngay cả khi bạn không có ý định biên dịch tất cả mã đó thành mã byte DEX. Đó là do d8 cần thông tin này để hiểu mã của dự án và phân giải các lệnh gọi đến các phương thức giao diện này.

Mã mẫu sau đây sẽ tạo một bản dựng tăng dần của một lớp có truy cập đến phương thức giao diện mặc định:

d8 MainActivity.class --intermediate --file-per-class --output ~/build/intermediate/dex
--lib android_sdk/platforms/api-level/android.jar
--classpath ~/build/javac/debug