Để bật tính năng tối ưu hoá ứng dụng, bạn phải sử dụng các thư viện tương thích với tính năng tối ưu hoá Android. Nếu một thư viện không được định cấu hình để tối ưu hoá cho Android (ví dụ: nếu thư viện đó sử dụng phản chiếu mà không đi kèm các quy tắc giữ lại liên kết), thì thư viện đó có thể không phù hợp với ứng dụng Android. Trang này giải thích lý do một số thư viện phù hợp hơn để tối ưu hoá ứng dụng và cung cấp các mẹo chung giúp bạn lựa chọn.
Mẹo chung khi chọn thư viện
Hãy sử dụng những mẹo này để đảm bảo các thư viện của bạn tương thích với tính năng tối ưu hoá ứng dụng.
Ưu tiên codegen hơn reflection
Chọn các thư viện sử dụng tính năng tạo mã (codegen) thay vì phản chiếu. Với codegen, trình tối ưu hoá có thể dễ dàng xác định mã nào thực sự được dùng trong thời gian chạy và mã nào có thể bị xoá. Bạn khó có thể biết liệu một thư viện có sử dụng codegen hay reflection, nhưng có một số dấu hiệu – hãy xem các mẹo để được trợ giúp.
Để biết thêm thông tin về codegen so với reflection, hãy xem phần Tối ưu hoá cho tác giả thư viện.
Kiểm tra việc sử dụng tính năng phản chiếu (nâng cao)
Bạn có thể biết liệu một thư viện có sử dụng chế độ phản chiếu hay không bằng cách kiểm tra mã của thư viện đó. Nếu thư viện sử dụng chế độ phản chiếu, hãy kiểm tra để đảm bảo thư viện cung cấp các quy tắc giữ lại được liên kết. Một thư viện có thể sử dụng tính năng phản chiếu nếu thư viện đó làm những việc sau:
- Sử dụng các lớp hoặc phương thức từ gói
kotlin.reflecthoặcjava.lang.reflect. - Sử dụng các hàm
Class.forNamehoặcclassLoader.getClass. - Đọc chú giải trong thời gian chạy, ví dụ: nếu lưu trữ giá trị chú giải bằng
val value = myClass.getAnnotation()hoặcval value = myMethod.getAnnotation()rồi thực hiện thao tác nào đó vớivalue. Gọi các phương thức bằng tên phương thức dưới dạng một chuỗi, như trong ví dụ sau:
// Calls the private `processData` API with reflection myObject.javaClass.getMethod("processData", DataType::class.java) ?.invoke(myObject, data)
Kiểm tra các vấn đề về việc tối ưu hoá
Khi cân nhắc một thư viện mới, hãy xem xét trình theo dõi lỗi và các cuộc thảo luận trực tuyến của thư viện để kiểm tra xem có vấn đề nào liên quan đến việc giảm thiểu hoặc định cấu hình tính năng tối ưu hoá ứng dụng hay không. Nếu có, bạn nên cố gắng tìm các lựa chọn thay thế cho thư viện đó. Hãy lưu ý những điều sau:
- Các thư viện AndroidX và các thư viện như Hilt hoạt động hiệu quả với việc tối ưu hoá ứng dụng vì chúng chủ yếu sử dụng codegen thay vì phản chiếu. Khi sử dụng tính năng phản chiếu, họ cung cấp các quy tắc lưu giữ tối thiểu để chỉ lưu giữ mã cần thiết.
- Các thư viện chuyển đổi tuần tự thường dùng tính năng phản chiếu để tránh mã nguyên mẫu khi khởi tạo hoặc chuyển đổi tuần tự các đối tượng. Thay vì các phương pháp dựa trên sự phản chiếu (chẳng hạn như Gson cho JSON), hãy tìm các thư viện sử dụng codegen để tránh những vấn đề này, ví dụ: bằng cách sử dụng Kotlin Serialization hoặc Moshi có codegen.
- Nếu có thể, hãy tránh dùng các thư viện có chứa quy tắc giữ lại trên toàn gói. Các quy tắc giữ lại trên toàn gói có thể giúp giải quyết lỗi, nhưng bạn nên tinh chỉnh các quy tắc giữ lại trên diện rộng để chỉ giữ lại mã cần thiết. Để biết thêm thông tin, hãy xem phần Áp dụng các hoạt động tối ưu hoá theo từng bước.
- Các thư viện không được yêu cầu bạn sao chép và dán quy tắc lưu giữ từ tài liệu vào một tệp trong dự án của bạn, đặc biệt là các quy tắc lưu giữ trên toàn gói. Về lâu dài, những quy tắc này sẽ trở thành gánh nặng bảo trì đối với nhà phát triển ứng dụng, đồng thời khó tối ưu hoá và thay đổi theo thời gian.
Bật tính năng tối ưu hoá sau khi thêm một thư viện mới
Khi bạn thêm một thư viện mới, hãy bật tính năng tối ưu hoá sau đó và kiểm tra xem có lỗi hay không. Nếu có lỗi, hãy tìm các lựa chọn thay thế cho thư viện đó hoặc viết các quy tắc giữ lại. Nếu một thư viện không tương thích với quy trình tối ưu hoá, hãy báo cáo lỗi cho thư viện đó.
Lọc ra các quy tắc giữ lại không hợp lệ (nâng cao)
Các quy tắc lưu giữ mang tính bổ sung. Điều này có nghĩa là một số quy tắc mà phần phụ thuộc thư viện bao gồm không thể bị xoá và có thể ảnh hưởng đến quá trình biên dịch các phần khác của ứng dụng. Ví dụ: nếu một thư viện có quy tắc vô hiệu hoá các hoạt động tối ưu hoá mã, thì quy tắc đó sẽ vô hiệu hoá các hoạt động tối ưu hoá cho toàn bộ dự án của bạn.
Bạn nên tránh các thư viện có quy tắc giữ lại mã giữ lại mã mà bạn thực sự nên xoá. Nhưng nếu phải sử dụng, bạn có thể lọc các quy tắc như minh hoạ trong mã sau:
// If you're using AGP 8.4 and higher
buildTypes {
release {
optimization.keepRules {
it.ignoreFrom("com.somelibrary:somelibrary")
}
}
}
// If you're using AGP 7.3-8.3
buildTypes {
release {
optimization.keepRules {
it.ignoreExternalDependencies("com.somelibrary:somelibrary")
}
}
}
Nghiên cứu điển hình: Tại sao Gson bị gián đoạn khi tối ưu hoá
Gson là một thư viện chuyển đổi tuần tự thường gây ra vấn đề về việc tối ưu hoá ứng dụng vì thư viện này sử dụng nhiều chức năng phản chiếu. Đoạn mã sau đây cho thấy cách Gson thường được dùng, có thể dễ dàng gây ra sự cố trong thời gian chạy. Lưu ý rằng khi dùng Gson để lấy danh sách các đối tượng Người dùng, bạn không gọi hàm tạo hoặc truyền một nhà máy đến hàm fromJson(). Việc tạo hoặc sử dụng các lớp do ứng dụng xác định mà không có một trong hai lớp sau đây là dấu hiệu cho thấy một thư viện có thể đang sử dụng tính năng phản chiếu không giới hạn:
- Lớp ứng dụng triển khai một thư viện, hoặc giao diện hoặc lớp tiêu chuẩn
- Trình bổ trợ tạo mã như KSP
class User(val name: String)
class UserList(val users: List<User>)
// This code runs in debug mode, but crashes when optimizations are enabled
Gson().fromJson("""[{"name":"myname"}]""", User::class.java).toString()
Để hiểu cách R8 hoạt động trên Gson, hãy xem Các quy tắc dành cho người dùng Gson. Khi phân tích mã này và không thấy UserList hoặc User được khởi tạo ở bất kỳ đâu, R8 có thể đổi tên các trường hoặc xoá những hàm khởi tạo có vẻ không được dùng, khiến ứng dụng của bạn gặp sự cố. Nếu đang sử dụng bất kỳ thư viện nào khác theo cách tương tự, bạn nên kiểm tra để đảm bảo rằng các thư viện đó sẽ không ảnh hưởng đến việc tối ưu hoá ứng dụng. Nếu có, hãy tránh sử dụng các thư viện đó.
Để xác định các lớp theo cách tương thích với các quy tắc của người dùng Gson, hãy sử dụng đoạn mã sau làm tài liệu tham khảo:
class User(@com.google.gson.annotations.SerializedName("name") val name: String)
class UserList(@com.google.gson.annotations.SerializedName("users") val users: List<User>)
Xin lưu ý rằng Room, Hilt và Moshi có codegen tạo các loại do ứng dụng xác định, nhưng sử dụng codegen để tránh nhu cầu phản ánh.