Trong quy trình phát triển Android hiện đại, việc cung cấp một ứng dụng nhỏ, nhanh và bảo mật là kỳ vọng cơ bản của người dùng. Công cụ chính của hệ thống xây dựng Android để đạt được điều này là trình tối ưu hoá R8 , trình biên dịch xử lý mã chết và xoá tài nguyên để rút gọn, đổi tên mã hoặc rút gọn và tối ưu hoá ứng dụng.
Bật R8 là một bước quan trọng trong quá trình chuẩn bị phát hành ứng dụng, nhưng nhà phát triển cần cung cấp hướng dẫn dưới dạng "Quy tắc giữ lại".
Sau khi đọc bài viết này, hãy xem video về Tuần lễ tiêu điểm về hiệu suất để biết cách bật, gỡ lỗi và khắc phục sự cố cho trình tối ưu hoá R8 trên YouTube.
Lý do cần có Quy tắc giữ lại
Nhu cầu viết Quy tắc giữ lại bắt nguồn từ một xung đột cốt lõi: R8 là một công cụ phân tích tĩnh, nhưng các ứng dụng Android thường dựa vào các mẫu thực thi động như phản chiếu hoặc lệnh gọi vào và ra khỏi mã gốc bằng JNI (Giao diện gốc Java).
R8 xây dựng biểu đồ về mã được sử dụng bằng cách phân tích các lệnh gọi trực tiếp. Khi mã được truy cập theo cách động, tính năng phân tích tĩnh của R8 không thể dự đoán điều đó và sẽ xác định mã đó là không dùng đến rồi xoá mã đó, dẫn đến sự cố trong thời gian chạy.
Quy tắc giữ lại là một hướng dẫn rõ ràng cho trình biên dịch R8, trong đó nêu rõ: "Lớp, phương thức hoặc trường cụ thể này là một điểm truy cập sẽ được truy cập linh động trong thời gian chạy. Bạn phải giữ lại, ngay cả khi không tìm thấy thông tin tham chiếu trực tiếp đến thông tin đó."
Hãy xem hướng dẫn chính thức để biết thêm thông tin chi tiết về Quy tắc giữ lại.
Nơi viết quy tắc giữ lại
Quy tắc giữ lại tuỳ chỉnh cho một ứng dụng được viết trong tệp văn bản. Theo quy ước, tệp này có tên là proguard-rules.pro và nằm trong thư mục gốc của ứng dụng hoặc mô-đun thư viện. Sau đó, tệp này được chỉ định trong loại bản dựng release của tệp build.gradle.kts của mô-đun.
release {
isShrinkResources = true
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro",
)
}Sử dụng đúng tệp mặc định
Phương thức getDefaultProguardFile nhập một bộ quy tắc mặc định do SDK Android cung cấp. Khi sử dụng tệp không chính xác, ứng dụng của bạn có thể không được tối ưu hoá. Hãy nhớ sử dụng proguard-android-optimize.txt. Tệp này cung cấp Quy tắc lưu giữ mặc định cho các thành phần Android tiêu chuẩn và bật tính năng tối ưu hoá mã của R8. proguard-android.txt đã lỗi thời chỉ cung cấp Quy tắc giữ lại nhưng không bật tính năng tối ưu hoá của R8.
Vì đây là một vấn đề nghiêm trọng về hiệu suất, nên chúng tôi bắt đầu cảnh báo nhà phát triển về việc sử dụng sai tệp, bắt đầu từ Bản cập nhật tính năng Narwhal 3 của Android Studio. Ngoài ra, kể từ Trình bổ trợ Android cho Gradle phiên bản 9.0, chúng tôi sẽ không còn hỗ trợ tệp proguard-android.txt đã lỗi thời. Vì vậy, hãy nhớ nâng cấp lên phiên bản tối ưu hoá.
Cách viết Quy tắc giữ lại
Quy tắc giữ lại bao gồm 3 phần chính:
- Một lựa chọn như
-keephoặc-keepclassmembers - Đối tượng sửa đổi không bắt buộc như
allowshrinking - Quy cách lớp xác định mã cần so khớp
Để biết cú pháp và ví dụ đầy đủ, hãy tham khảo hướng dẫn về cách thêm Quy tắc giữ lại.
Mẫu phản tác dụng của quy tắc Keep
Điều quan trọng là bạn phải biết về các phương pháp hay nhất cũng như các mẫu phản thiết kế. Những mẫu phản thiết kế này thường phát sinh do hiểu lầm hoặc do các lối tắt khắc phục sự cố và có thể gây ảnh hưởng nghiêm trọng đến hiệu suất của bản dựng chính thức.
Tuỳ chọn chung
Các cờ này là nút bật/tắt chung và không bao giờ được dùng trong bản phát hành. Các lệnh này chỉ dùng để gỡ lỗi tạm thời nhằm tách biệt một vấn đề.
Việc sử dụng -dontotptimize sẽ vô hiệu hoá hiệu quả các tính năng tối ưu hoá hiệu suất của R8, khiến ứng dụng chạy chậm hơn.
Khi sử dụng -dontobfuscate, bạn sẽ tắt tất cả tính năng đổi tên và khi sử dụng -dontshrink, bạn sẽ tắt tính năng xoá mã chết. Cả hai quy tắc chung này đều làm tăng kích thước ứng dụng.
Tránh sử dụng các cờ chung này trong môi trường thực tế bất cứ khi nào có thể để mang lại trải nghiệm hiệu quả hơn cho người dùng ứng dụng.
Quy tắc giữ lại quá rộng
Cách dễ nhất để vô hiệu hoá lợi ích của R8 là viết Quy tắc giữ lại quá rộng. Các quy tắc giữ lại như quy tắc bên dưới hướng dẫn trình tối ưu hoá R8 không rút gọn, không làm rối mã nguồn và không tối ưu hoá bất kỳ lớp nào trong gói này hoặc bất kỳ gói con nào của gói này. Thao tác này sẽ xoá hoàn toàn các lợi ích của R8 đối với toàn bộ gói đó. Thay vào đó, hãy thử viết các Quy tắc giữ lại cụ thể và hẹp.
-keep class com.example.package.** { *;} // WIDE KEEP RULES CAUSE PROBLEMSToán tử đảo ngược (!)
Toán tử đảo ngược (!) có vẻ là một cách hiệu quả để loại trừ một gói khỏi quy tắc. Nhưng mọi chuyện không đơn giản như vậy. Hãy xem ví dụ sau:
-keep class !com.example.my_package.** { *; } // USE WITH CAUTIONBạn có thể nghĩ rằng quy tắc này có nghĩa là "không giữ lại các lớp trongcom.example.package". Nhưng thực ra, quy tắc này có nghĩa là "giữ lại mọi lớp, phương thức và thuộc tính trong toàn bộ ứng dụng không nằm trong com.example.package". Nếu điều này khiến bạn bất ngờ, tốt nhất bạn nên kiểm tra xem có bất kỳ phủ định nào trong cấu hình R8 của mình hay không.
Quy tắc thừa cho các thành phần Android
Một lỗi phổ biến khác là thêm thủ công Quy tắc giữ lại cho Activities, Services hoặc BroadcastReceivers của ứng dụng. Điều này là không cần thiết. Tệp proguard-android-optimize.txt mặc định đã bao gồm các quy tắc liên quan để các thành phần Android chuẩn này hoạt động ngay lập tức.
Ngoài ra, nhiều thư viện cũng có Quy tắc giữ lại riêng. Vì vậy, bạn không cần phải viết quy tắc riêng cho các quy tắc này. Trong trường hợp có vấn đề với Quy tắc giữ lại của thư viện mà bạn đang sử dụng, tốt nhất bạn nên liên hệ với tác giả thư viện để xem vấn đề là gì.
Các phương pháp hay nhất về quy tắc lưu giữ
Giờ thì bạn đã biết những điều không nên làm, hãy cùng tìm hiểu về các phương pháp hay nhất.
Viết Quy tắc giữ lại có phạm vi hẹp
Quy tắc giữ lại hiệu quả phải càng hẹp và cụ thể càng tốt. Các quy tắc này chỉ nên giữ lại những gì cần thiết, cho phép R8 tối ưu hoá mọi thứ khác.
| Quy tắc | Chất lượng |
|---|---|
| Thấp: Giữ lại toàn bộ gói và các gói con của gói đó |
| Thấp: Giữ lại toàn bộ lớp, có thể vẫn còn quá rộng |
-keepclassmembers class com.example.MyClass {
private java.lang.String secretMessage;
public void onNativeEvent(java.lang.String);
} | Cao: Chỉ giữ lại các phương thức và thuộc tính có liên quan trong một lớp cụ thể |
Sử dụng các đối tượng cấp trên chung
Thay vì viết các Quy tắc giữ lại riêng biệt cho nhiều mô hình dữ liệu khác nhau, hãy viết một quy tắc nhắm đến một lớp cơ sở hoặc giao diện chung. Quy tắc dưới đây yêu cầu R8 giữ lại mọi thành phần của các lớp triển khai giao diện này và có khả năng mở rộng cao.
# Keep all fields of any class that implements SerializableModel
-keepclassmembers class * implements com.example.models.SerializableModel {
<fields>;
}Sử dụng chú thích để nhắm đến nhiều lớp
Tạo chú thích tuỳ chỉnh (ví dụ: @Serialize) và sử dụng chú thích đó để "gắn thẻ" các lớp cần giữ nguyên trường. Đây là một mẫu khác rõ ràng, mang tính khai báo và có khả năng mở rộng cao. Bạn cũng có thể tạo Quy tắc giữ lại cho các chú thích hiện có từ các khung mà bạn đang sử dụng.
# Keep all fields of any class annotated with @Serialize
-keepclassmembers class * {
@com.example.annotations.Serialize <fields>;
}Chọn chế độ giữ lại phù hợp
Lựa chọn giữ lại là phần quan trọng nhất của quy tắc. Việc chọn sai có thể vô tình tắt tính năng tối ưu hoá.
| Lựa chọn giữ lại | Chức năng |
-keep | Ngăn việc xoá hoặc đổi tên lớp và các thành phần được đề cập trong phần khai báo . |
-keepclassmembers | Ngăn việc xoá hoặc đổi tên các thành phần được chỉ định, nhưng cho phép xoá lớp, nhưng chỉ đối với các lớp không bị xoá. |
-keepclasseswithmembers | Kết hợp: Giữ lại lớp và các thành phần của lớp đó, chỉ khi tất cả các thành phần được chỉ định đều có mặt. |
Bạn có thể tìm hiểu thêm về tuỳ chọn giữ lại trong tài liệu về Tuỳ chọn giữ lại.
Cho phép tối ưu hoá bằng Hệ số điều chỉnh
Các đối tượng sửa đổi như allowshrinking và allowobfuscation sẽ nới lỏng quy tắc -keep rộng, trả lại khả năng tối ưu hoá cho R8. Ví dụ: nếu một thư viện cũ buộc bạn phải sử dụng -keep trên toàn bộ lớp, thì bạn có thể lấy lại một số tính năng tối ưu hoá bằng cách cho phép rút gọn và làm rối mã nguồn:
# Keep this class, but allow R8 to remove it if it's unused and allow R8 to rename it. -keep,allowshrinking,allowobfuscation class com.example.LegacyClass
Thêm các lựa chọn chung cho hoạt động tối ưu hoá bổ sung
Ngoài Quy tắc giữ lại, bạn có thể thêm cờ chung vào tệp cấu hình R8 để tối ưu hoá hơn nữa.
-repackageclasses là một tuỳ chọn mạnh mẽ hướng dẫn R8 di chuyển tất cả các lớp bị làm rối mã nguồn vào một gói duy nhất. Việc này giúp tiết kiệm đáng kể dung lượng trong tệp DEX bằng cách xoá các chuỗi tên gói thừa.
-allowaccessmodification cho phép R8 mở rộng quyền truy cập (ví dụ: private thành public) để cho phép chèn cùng dòng linh hoạt hơn. Tính năng này hiện được bật theo mặc định khi sử dụng proguard-android-optimize.txt.
Cảnh báo: Tác giả thư viện tuyệt đối không được thêm các cờ tối ưu hoá chung này vào quy tắc của người dùng, vì các cờ này sẽ được áp dụng bắt buộc cho toàn bộ ứng dụng.
Để rõ ràng hơn, trong phiên bản 9.0 của Trình bổ trợ Android cho Gradle, chúng tôi sẽ bắt đầu bỏ qua hoàn toàn các cờ tối ưu hoá chung của thư viện.
Các phương pháp hay nhất cho thư viện
Mọi ứng dụng Android đều dựa vào thư viện theo cách này hay cách khác. Vì vậy, hãy cùng tìm hiểu các phương pháp hay nhất cho thư viện.
Dành cho nhà phát triển thư viện
Nếu thư viện của bạn sử dụng phản chiếu hoặc JNI, thì bạn có trách nhiệm cung cấp các Quy tắc lưu giữ cần thiết cho người dùng. Các quy tắc này được đặt trong tệp consumer-rules.pro, sau đó được tự động đóng gói bên trong tệp AAR của thư viện.
android {
defaultConfig {
consumerProguardFiles("consumer-rules.pro")
}
...
}Dành cho người dùng thư viện
Lọc ra các Quy tắc giữ lại có vấn đề
Nếu phải sử dụng một thư viện có chứa Quy tắc lưu giữ có vấn đề, bạn có thể lọc các quy tắc đó trong tệp build.gradle.kts kể từ AGP 9.0. Thao tác này sẽ yêu cầu R8 bỏ qua các quy tắc đến từ một phần phụ thuộc cụ thể.
release {
optimization.keepRules {
// Ignore all consumer rules from this specific library
it.ignoreFrom("com.somelibrary:somelibrary")
}
}Quy tắc giữ lại tốt nhất là không có quy tắc giữ lại
Chiến lược cấu hình R8 tối ưu là loại bỏ hoàn toàn nhu cầu viết Quy tắc giữ lại. Đối với nhiều ứng dụng, bạn có thể đạt được điều này bằng cách chọn các thư viện hiện đại ưu tiên tạo mã hơn là phản chiếu. Với tính năng tạo mã, trình tối ưu hoá có thể dễ dàng xác định mã nào thực sự được sử dụng trong thời gian chạy và mã nào có thể bị xoá. Ngoài ra, việc không sử dụng bất kỳ phản chiếu động nào đồng nghĩa với việc không có điểm truy cập "ẩn" và do đó, không cần Quy tắc giữ lại. Khi chọn một thư viện mới, hãy luôn ưu tiên giải pháp sử dụng tính năng tạo mã thay vì phản chiếu.
Để biết thêm thông tin về cách chọn thư viện, hãy xem bài viết chọn thư viện một cách thông minh.
Gỡ lỗi và khắc phục sự cố cấu hình R8
Khi R8 xoá mã mà lẽ ra phải giữ lại hoặc khi tệp APK của bạn lớn hơn dự kiến, hãy sử dụng các công cụ này để chẩn đoán vấn đề.
Tìm quy tắc Keep trùng lặp và chung
Vì R8 hợp nhất các quy tắc từ hàng chục nguồn, nên có thể khó biết được bộ quy tắc "cuối cùng" là gì. Việc thêm cờ này vào tệp proguard-rules.pro sẽ tạo ra một báo cáo đầy đủ:
# Outputs the final, merged set of rules to the specified file -printconfiguration build/outputs/logs/configuration.txt
Bạn có thể tìm kiếm tệp này để tìm các quy tắc thừa hoặc truy xuất một quy tắc có vấn đề (chẳng hạn như -dontoptimize) về thư viện cụ thể chứa quy tắc đó.
Hỏi R8: Tại sao bạn giữ lại thông tin này?
Nếu một lớp mà bạn muốn xoá vẫn còn trong ứng dụng, thì R8 có thể cho bạn biết lý do. Chỉ cần thêm quy tắc này:
# Asks R8 to explain why it's keeping a specific class class com.example.MyUnusedClass -whyareyoukeeping
Trong quá trình tạo bản dựng, R8 sẽ in chính xác chuỗi tham chiếu khiến lớp đó được giữ lại, cho phép bạn theo dõi tham chiếu và điều chỉnh các quy tắc của mình.
Để xem hướng dẫn đầy đủ, hãy xem phần khắc phục sự cố R8.
Các bước tiếp theo
R8 là một công cụ mạnh mẽ giúp nâng cao hiệu suất của ứng dụng Android. Hiệu quả của công cụ này phụ thuộc vào việc bạn có hiểu đúng cách hoạt động của công cụ này dưới dạng công cụ phân tích tĩnh hay không.
Bằng cách viết các quy tắc cụ thể ở cấp thành viên, tận dụng các đối tượng cấp trên và chú thích, đồng thời lựa chọn cẩn thận các tuỳ chọn giữ lại phù hợp, bạn có thể giữ lại chính xác những gì cần thiết. Phương pháp nâng cao nhất là loại bỏ hoàn toàn nhu cầu về quy tắc bằng cách chọn các thư viện hiện đại, dựa trên mã tạo sẵn thay vì các thư viện dựa trên phản chiếu trước đó.
Trong khi theo dõi Tuần lễ Tiêu điểm về hiệu suất, hãy nhớ xem video của Tuần lễ Tiêu điểm hôm nay trên YouTube và tiếp tục tham gia thử thách R8 của chúng tôi. Sử dụng #optimizationEnabled cho mọi câu hỏi về cách bật hoặc khắc phục sự cố R8. Chúng tôi sẵn sàng trợ giúp.
Đã đến lúc bạn tự mình trải nghiệm những lợi ích này.
Chúng tôi khuyến khích bạn bật chế độ đầy đủ của R8 cho ứng dụng của mình ngay hôm nay.
- Hãy làm theo hướng dẫn dành cho nhà phát triển để bắt đầu: Bật tính năng tối ưu hoá ứng dụng.
- Kiểm tra xem bạn có còn sử dụng
proguard-android.txthay không và thay thế bằngproguard-android-optimize.txt. - Sau đó, đo lường tác động. Đừng chỉ cảm nhận sự khác biệt, hãy xác minh điều đó. Đo lường mức tăng hiệu suất bằng cách điều chỉnh mã từ ứng dụng mẫu Macrobenchmark trên GitHub để đo lường thời gian khởi động trước và sau khi tối ưu hoá.
Chúng tôi tin rằng bạn sẽ thấy hiệu suất ứng dụng của mình cải thiện đáng kể.
Trong khi tham gia, hãy dùng thẻ mạng xã hội #AskAndroid để đặt câu hỏi. Trong suốt tuần, các chuyên gia của chúng tôi sẽ theo dõi và trả lời các câu hỏi của bạn.
Hãy đón xem vào ngày mai, chúng tôi sẽ nói về tính năng Tối ưu hoá theo hướng dẫn của hồ sơ bằng Hồ sơ cơ sở và Hồ sơ khởi động, chia sẻ cách cải thiện hiệu suất kết xuất của Compose trong các bản phát hành trước đây và chia sẻ những điểm cần lưu ý về hiệu suất cho công việc trong nền.
-
Tin tức về sản phẩmTrong kỷ nguyên trí tuệ nhân tạo, người dùng không còn chỉ thao tác trên các ứng dụng mà còn thể hiện những mục tiêu cấp cao và mong đợi các tác nhân AI như Gemini hoàn thành các quy trình làm việc nhiều bước, đôi khi trên nhiều ứng dụng trên thiết bị của họ.
Jingyu Shi • Thời gian đọc: 4 phút -
Tin tức về sản phẩmHôm nay, tại Hội nghị Nhà phát triển Android, chúng tôi xin thông báo về việc cung cấp rộng rãi nhiều công cụ và API được thiết kế để loại bỏ những trở ngại hằng ngày và giúp bạn linh hoạt hơn trong việc xây dựng các ứng dụng Android tuyệt vời.
Matthew McCullough • Thời gian đọc: 4 phút -
Tin tức về sản phẩmAndroid Studio Rabbit 2 hiện đã ổn định và sẵn sàng để bạn sử dụng trong môi trường thực tế. Bản phát hành này bao gồm các tính năng chính giúp cải thiện quá trình phát triển của bạn, chẳng hạn như Bring Your Own Agent (Sử dụng tác nhân của riêng bạn), các tính năng mới của Trình mô phỏng XR, bản cập nhật LeakCanary, công cụ quản lý kỹ năng mới của tác nhân, tính năng xem xét mã bằng AI và nhiều tính năng khác.
Matthew Warner • Đọc trong 3 phút
Nhận thông tin chi tiết mới nhất về hoạt động phát triển Android trong hộp thư đến của bạn hằng tuần.