Giới thiệu về quy tắc giữ lại

Khi bạn bật tính năng tối ưu hoá ứng dụng bằng chế độ cài đặt mặc định, R8 sẽ thực hiện các hoạt động tối ưu hoá mở rộng để tối đa hoá lợi ích về hiệu suất. R8 thực hiện những sửa đổi đáng kể đối với mã, bao gồm cả việc đổi tên, di chuyển và xoá các lớp, trường và phương thức. Nếu nhận thấy những nội dung sửa đổi này gây ra lỗi, bạn cần chỉ định những phần mã mà R8 không nên sửa đổi bằng cách khai báo những phần đó trong quy tắc lưu giữ.

Các trường hợp thường gặp cần có quy tắc lưu giữ

R8 xác định và giữ lại tất cả lệnh gọi trực tiếp trong mã của bạn. Tuy nhiên, R8 không thể thấy các trường hợp sử dụng mã gián tiếp, điều này có thể khiến R8 xoá mã mà ứng dụng của bạn cần, gây ra sự cố. Sử dụng quy tắc giữ lại để yêu cầu R8 giữ lại mã được sử dụng gián tiếp như vậy. Sau đây là một số trường hợp phổ biến mà bạn có thể cần đến quy tắc lưu giữ:

  • Mã được truy cập bằng cơ chế phản chiếu: R8 không thể xác định thời điểm các lớp, trường hoặc phương thức được truy cập bằng cơ chế phản chiếu. Ví dụ: R8 không thể xác định một phương thức được tra cứu theo tên bằng Class.getDeclaredMethod() hoặc chú thích được truy xuất bằng Class.getAnnotation(). Trong những trường hợp này, R8 có thể đổi tên các phương thức và chú thích này hoặc xoá hoàn toàn, dẫn đến ClassNotFoundException hoặc NoSuchMethodException trong thời gian chạy.
  • Mã được gọi từ Giao diện gốc Java (JNI): Khi mã gốc (C hoặc C++) gọi một phương thức Java hoặc Kotlin, hoặc mã Java hoặc Kotlin gọi mã C++ bằng JNI, lệnh gọi sẽ dựa trên việc tra cứu chuỗi động tên của phương thức. R8 không thể thấy lệnh gọi phương thức dựa trên chuỗi động, vì vậy, các hoạt động tối ưu hoá của R8 có thể làm hỏng mã của bạn.

Đây không phải là danh sách đầy đủ các trường hợp cần có quy tắc giữ lại, nhưng những trường hợp này bao gồm hầu hết các trường hợp mà bạn có thể cần đến quy tắc giữ lại.

Nếu mã của bạn sử dụng tính năng phản chiếu hoặc được gọi từ JNI, thì bạn có thể chú thích mã đó bằng chú thích Jetpack Keep thay vì viết quy tắc lưu giữ theo cách thủ công. Trình bổ trợ Gradle của thư viện này sẽ tạo ra các quy tắc giữ lại từ chú thích tại thời điểm tạo.

Tìm hiểu mức độ ảnh hưởng của quy tắc giữ lại đối với việc tối ưu hoá

R8 là một trình tối ưu hoá toàn bộ chương trình, phân tích mã ứng dụng của bạn để xoá mã không dùng đến, phương thức cùng dòng, hợp nhất các lớp và làm rối mã nguồn giá trị nhận dạng. Tuy nhiên, R8 chỉ có thể tối ưu hoá trong cấu hình mà bạn xác định.

Quy tắc giữ lại là hướng dẫn rõ ràng để giới hạn việc tối ưu hoá. Mỗi quy tắc giữ lại mà bạn khai báo sẽ hạn chế khả năng của R8 trong việc chuyển đổi phần đó trong toàn bộ mã nguồn của bạn. Giá trị của một quy tắc giữ lại được thiết kế tốt nằm ở tính cụ thể của quy tắc đó:

  • Quy tắc giữ lại hẹp cho phép tối ưu hoá tối đa: Bằng cách chỉ nhắm đến các điểm truy cập chính xác được truy cập linh động (chẳng hạn như hàm khởi tạo phản chiếu hoặc trường được chuyển đổi tuần tự), bạn cho phép R8 tối ưu hoá phần còn lại của mã.

  • Quy tắc giữ lại rộng ngăn chặn việc tối ưu hoá: Việc khai báo các quy tắc trên toàn gói hoặc ký tự đại diện rộng sẽ ngăn R8 tối ưu hoá toàn bộ lớp và tất cả thành phần của lớp. R8 không thể xoá, cùng dòng, hợp nhất hoặc làm rối mã đó.

Các quy tắc giữ lại quá rộng sẽ trực tiếp dẫn đến tệp nhị phân DEX bị phình to, mức sử dụng bộ nhớ của ứng dụng tăng lên, thời gian khởi động chậm hơn (TTID và TTFD) và tỷ lệ ANR cao hơn. Mục tiêu của bạn không phải là giữ lại các lớp "để phòng hờ", mà là xác định hợp đồng tối thiểu cần thiết để thực thi động.

Để tìm các quy tắc giữ lại rộng hoặc không cần thiết, bao gồm cả các quy tắc từ thư viện bên thứ ba, hãy sử dụng Trình phân tích cấu hình R8. Công cụ này báo cáo điểm số về việc rút gọn, tối ưu hoá và làm rối mã nguồn, cho biết mức độ tối ưu hoá mã mà R8 có thể thực hiện.

Cách thêm quy tắc giữ lại vào ứng dụng

Cấu hình quy tắc giữ lại phụ thuộc vào phiên bản AGP bạn sử dụng.

Các ứng dụng lớn hơn thường có mã trong nhiều mô-đun thư viện. Trong những trường hợp như vậy, bạn nên đặt các quy tắc giữ lại cùng với mã mà các quy tắc đó áp dụng trong mô-đun thư viện cụ thể. Sự khác biệt quan trọng trong việc duy trì các quy tắc giữ lại cho thư viện nằm ở cách bạn khai báo các quy tắc này trong tệp build.gradle.kts (hoặc build.gradle) của mô-đun thư viện. Hãy xem phần Tối ưu hoá cho tác giả thư viện để tìm hiểu thêm.

Đối với AGP phiên bản 9.3 trở lên

Thêm các quy tắc của bạn vào một tệp có hậu tố .keep trong thư mục src/<variant>/keepRules. Ví dụ: src/main/keepRules/custom-rules.keep Xin lưu ý rằng việc sử dụng DSL đã cập nhật cho AGP 9.3 trở lên sẽ cho phép tối ưu hoá cả mã và tài nguyên, đồng thời không yêu cầu bạn xác định rõ ràng tệp quy tắc giữ lại mặc định của Android (proguard-android-optimize.txt) vì tệp này được đưa vào theo mặc định, trừ phi bị bỏ qua một cách rõ ràng.

Kotlin

android {
    buildTypes {
        release {
            optimization {
                enable = true // Enables code and resource optimizations.
            }
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            optimization {
                enable = true // Enables code and resource optimizations.
            }
        }
    }
}

Bỏ qua các quy tắc giữ lại mặc định của nền tảng Android (Nâng cao)

AGP cung cấp một bộ quy tắc mặc định cho nền tảng Android. Để bỏ qua các quy tắc này khỏi cấu hình của dự án và tự quản lý tất cả quy tắc giữ lại, hãy đặt optimization.keepRules.includeDefault thành false.

Kotlin

android {
    buildTypes {
        release {
            optimization {
                enable = true // Enables code and resource optimizations.
                keepRules {
                    includeDefault = false // The file with optimization
                    // rules for the Android platform is included by
                    // default. To omit this file, set this option to false.
                }
            }
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            optimization {
                enable = true // Enables code and resource optimizations.
                keepRules {
                    includeDefault = false // The file with optimization
                    // rules for the Android platform is included by
                    // default. To omit this file, set this option to false.
                }
            }
        }
    }
}

DSL cũ cho các phiên bản AGP thấp hơn 9.3

Thêm các quy tắc của bạn vào tệp proguard-rules.pro nằm trong thư mục gốc của mô-đun ứng dụng. Tệp này có thể đã có sẵn, nhưng nếu chưa có, hãy tạo tệp. Để áp dụng các quy tắc trong tệp, bạn phải khai báo tệp trong tệp build.gradle.kts (hoặc build.gradle) ở cấp mô-đun.

Theo mặc định, tệp bản dựng cũng bao gồm tệp proguard-android-optimize.txt. Tệp này chứa các quy tắc bắt buộc đối với hầu hết các dự án Android, vì vậy, bạn nên giữ tệp này trong tệp bản dựng. Tệp này dựa trên và chia sẻ nội dung với tệp proguard-common.txt.

DSL cũ (Kotlin)

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            isShrinkResources = true

            proguardFiles(
                // Default file with default optimization rules.
                getDefaultProguardFile("proguard-android-optimize.txt"),
                ...
            )
        }
    }
}

DSL cũ (Groovy)

android {
    buildTypes {
        release {
            minifyEnabled = true
            shrinkResources = true

            // Default file with default optimization rules.
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt')
            ...
        }
    }
}

Thêm quy tắc giữ lại

Khi thêm quy tắc giữ lại, bạn có thể thêm các lựa chọn chung cũng như xác định quy tắc giữ lại của riêng mình.

  • Tuỳ chọn chung: Tuỳ chọn chung là các lệnh chung ảnh hưởng đến cách R8 hoạt động trên toàn bộ mã nguồn của bạn. Để tìm hiểu thêm, hãy xem phần Tuỳ chọn chung.

  • Quy tắc giữ lại: Bạn cần thiết kế quy tắc giữ lại một cách cẩn thận để đảm bảo cân bằng giữa việc tối đa hoá khả năng tối ưu hoá mã mà không vô tình làm hỏng ứng dụng. Để tìm hiểu cách viết quy tắc giữ lại, hãy xem phần Thêm quy tắc giữ lại.

Quy tắc giữ lại cho tác giả thư viện

Sau khi tìm hiểu về các lựa chọn chung và cú pháp cho quy tắc giữ lại, hãy xem phần Tối ưu hoá cho tác giả thư viện để biết thêm thông tin chi tiết.