Thêm 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á chuyên sâu để tối đa hoá lợi ích về hiệu suất. R8 thực hiện các 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 trường và phương thức của lớp. Nếu việc này gây ra lỗi, bạn cần chỉ định những phần mã cần giữ lại bằng cách viết quy tắc giữ lại.

R8 có thể xoá hoặc sửa đổi mã không chính xác trong các trường hợp sau:

  • Phản chiếu: mã được truy cập bằng tính năng phản chiếu, ví dụ: sử dụng Class.forName() hoặc Method.invoke(). R8 thường không thể biết lớp hoặc phương thức nào sẽ được truy cập theo cách này.
  • Tuần tự hoá: các lớp hoặc trường cần thiết cho quá trình tuần tự hoá và huỷ tuần tự hoá có vẻ như không được sử dụng cho R8 (đây là một hình thức phản chiếu khác).
  • Giao diện gốc Java (JNI): Các phương thức Java được gọi từ mã gốc. R8 không phân tích mã gốc để xem mã gốc có thể gọi lại vào Java hay không.

Trang này trình bày cách giới hạn phạm vi tối ưu hoá của R8. Để tìm hiểu cách tuỳ chỉnh tài nguyên cần giữ lại, hãy xem phần Thêm quy tắc giữ lại cho tài nguyên.

Vị trí thêm quy tắc giữ lại

Bạn nên thêm các quy tắc vào tệp proguard-rules.pro nằm trong thư mục gốc của mô-đun (tệp có thể đã có sẵn, nhưng nếu không, 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 như trong mã sau:

Kotlin

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

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

                // File with your custom rules.
                "proguard-rules.pro"
            )
            ...
        }
    }
    ...
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            shrinkResources true

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

                // File with your custom rules.
                'proguard-rules.pro'
            )
        }
    }
    // ...
}

Theo mặc định, tập lệnh bản dựng của bạn cũng bao gồm tệp proguard-android-optimize.txt. Tệp này bao gồm 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 lệnh bản dựng.

Cách viết quy tắc giữ lại

Trong quá trình biên dịch ứng dụng, R8 sẽ phát hiện mã cần được giữ lại trong ứng dụng bằng cách phân tích biểu đồ lệnh gọi của ứng dụng, bắt đầu từ các mục nhập tệp kê khai (chẳng hạn như hoạt động hoặc dịch vụ) và theo dõi qua mọi lệnh gọi hàm ứng dụng và thư viện. R8 xoá mã không được tham chiếu trực tiếp theo cách này, điều này có thể gây ra sự cố nếu mã được thực thi không thuộc biểu đồ này, ví dụ: mã được gọi bằng tính năng phản chiếu. Bằng cách viết quy tắc giữ lại của riêng mình, bạn có thể thông báo cho R8 về mã cần giữ lại trong ứng dụng.

Để thêm quy tắc giữ lại, hãy thêm dòng -keep trong tệp proguard-rules.pro.

Giữ lại cú pháp quy tắc

Quy tắc giữ thường tuân theo định dạng sau:

-<KeepOption> [OptionalModifier,...] <ClassSpecification> [{ OptionalMemberSpecification }]

Ví dụ: để giữ lại một lớp cụ thể và tất cả thành viên của lớp đó, hãy sử dụng mã sau:

-keep class com.myapp.MyClass { *; }

Để xem thêm ví dụ, hãy xem phần ví dụ.

Dưới đây là chức năng của các thành phần quy tắc giữ lại:

  • <KeepOption> cho phép bạn chỉ định những khía cạnh của một lớp cần giữ lại:

    Tuỳ chọn giữ lại Mô tả

    -keep

    Duy trì lớp và các thành viên được liệt kê trong [{ OptionalMemberSpecification }].

    -keepclassmembers

    Cho phép tối ưu hoá lớp; nếu lớp được giữ nguyên, hãy giữ nguyên các thành viên được liệt kê trong [{ OptionalMemberSpecification }].

    -keepnames

    Cho phép xoá lớp và thành viên, nhưng không làm rối mã nguồn hoặc sửa đổi theo cách khác.

    -keepclassmembernames

    Cho phép xoá lớp và thành viên, nhưng không làm rối mã nguồn hoặc sửa đổi thành viên theo cách khác.

    -keepclasseswithmembers

    Không xoá hoặc làm rối mã nguồn của các lớp nếu các thành viên khớp với mẫu đã chỉ định.

    Bạn nên sử dụng -keepclassmembers vì lớp này cho phép tối ưu hoá nhiều nhất, sau đó là -keepnames nếu cần. -keep không cho phép bất kỳ tính năng tối ưu hoá nào, vì vậy, hãy cố gắng sử dụng tính năng này một cách tiết kiệm.

  • [OptionalModifier],...] cho phép bạn liệt kê không có hoặc nhiều đối tượng sửa đổi ngôn ngữ Java của một lớp, ví dụ: public hoặc final.

  • <ClassSpecification> cho phép bạn chỉ định lớp (hoặc siêu lớp hoặc giao diện đã triển khai) mà quy tắc giữ lại sẽ áp dụng. Trong trường hợp đơn giản nhất, đây là một lớp đủ điều kiện.

  • [{ OptionalMemberSpecification }] cho phép bạn lọc hành vi giữ lại để chỉ các lớp và phương thức khớp với một số mẫu nhất định. Nhìn chung, bạn nên sử dụng tính năng này trong hầu hết các quy tắc giữ lại để tránh giữ lại nhiều hơn dự kiến.

Giữ lại ví dụ về quy tắc

Sau đây là một số ví dụ về quy tắc giữ lại được thiết kế tốt.

Tạo lớp con

Quy tắc giữ này được đóng gói bên trong androidx.room:room-runtime để giữ cho các hàm khởi tạo cơ sở dữ liệu được tạo bản sao bằng tính năng phản chiếu.

-keep class * extends androidx.room.RoomDatabase { void <init>(); }

Phản chiếu từ Khung Android ObjectAnimator

Quy tắc giữ này được đóng gói bên trong androidx.vectordrawable:vectordrawable-animated để cho phép ObjectAnimator gọi vào phương thức getter hoặc setter từ mã gốc bằng JNI. Xin lưu ý rằng các hệ thống ảnh động mới hơn trong Compose không yêu cầu các quy tắc giữ nguyên như thế này, đây chỉ là một ví dụ về cấu trúc quy tắc.

-keepclassmembers class androidx.vectordrawable.graphics.drawable.VectorDrawableCompat$* {
   void set*(***);
   *** get*();
}

Đăng ký JNI

Quy tắc giữ này được đóng gói bên trong androidx.graphics:graphics-path, dùng để giữ các phương thức gốc. Điều này có thể cần thiết nếu thư viện của bạn đăng ký các phương thức gốc theo cách thủ công bằng env->RegisterNatives().

-keepclasseswithmembers class androidx.graphics.path.** {
    native <methods>;
}