Tối ưu hoá cho tác giả thư viện

Là tác giả thư viện, bạn phải đảm bảo rằng nhà phát triển ứng dụng có thể dễ dàng tích hợp thư viện của bạn vào ứng dụng của họ trong khi vẫn duy trì trải nghiệm chất lượng cao cho người dùng cuối. Bạn nên đảm bảo rằng thư viện của mình tương thích với tính năng tối ưu hoá Android mà không cần thiết lập thêm hoặc ghi nhận rằng thư viện có thể không phù hợp để sử dụng trên Android.

Tài liệu này nhắm đến nhà phát triển của các thư viện đã phát hành, nhưng cũng có thể hữu ích cho nhà phát triển của các mô-đun thư viện nội bộ trong một ứng dụng lớn, được mô-đun hoá.

Nếu bạn là nhà phát triển ứng dụng và muốn tìm hiểu cách tối ưu hoá ứng dụng Android, hãy xem bài viết Bật tính năng tối ưu hoá ứng dụng. Để tìm hiểu về những thư viện phù hợp để sử dụng, hãy xem phần Chọn thư viện một cách khôn ngoan.

Sử dụng codegen thay vì phản chiếu

Khi có thể, hãy sử dụng tính năng tạo mã (codegen) thay vì phản chiếu. Tạo mã và phản chiếu đều là những phương pháp phổ biến để tránh mã nguyên mẫu khi lập trình, nhưng codegen tương thích hơn với trình tối ưu hoá ứng dụng như R8:

  • Với codegen, mã được phân tích và sửa đổi trong quá trình tạo bản dựng. Vì không có bất kỳ sửa đổi lớn nào sau thời gian biên dịch, trình tối ưu hoá sẽ biết mã nào cần thiết và mã nào có thể xoá một cách an toàn.
  • Với tính năng phản chiếu, mã được phân tích và thao tác trong thời gian chạy. Vì mã này chưa thực sự hoàn tất cho đến khi thực thi, nên trình tối ưu hoá không biết mã nào có thể được xoá một cách an toàn. Việc này có thể xoá mã được sử dụng động thông qua phản chiếu trong thời gian chạy, gây ra sự cố ứng dụng cho người dùng.

Nhiều thư viện hiện đại sử dụng codegen thay vì phản chiếu. Xem KSP để biết một điểm truy cập phổ biến, được Room, Dagger2 và nhiều ứng dụng khác sử dụng.

Trường hợp có thể phản chiếu

Nếu phải sử dụng tính năng phản chiếu, bạn chỉ nên phản chiếu vào một trong các thành phần sau:

  • Các loại mục tiêu cụ thể (các trình triển khai giao diện hoặc lớp con cụ thể)
  • Mã sử dụng chú giải thời gian chạy cụ thể

Việc sử dụng tính năng phản chiếu theo cách này sẽ giới hạn chi phí thời gian chạy và cho phép ghi các quy tắc giữ lại của người dùng được nhắm mục tiêu.

Hình thức phản chiếu cụ thể và có mục tiêu này là một mẫu mà bạn có thể thấy trên cả khung Android (ví dụ: khi tăng cường các hoạt động, thành phần hiển thị và đối tượng có thể vẽ) và thư viện AndroidX (ví dụ: khi tạo WorkManager ListenableWorkers hoặc RoomDatabases). Ngược lại, tính năng phản chiếu mở của Gson không phù hợp để sử dụng trong các ứng dụng Android.

Viết quy tắc giữ lại của người tiêu dùng

Thư viện nên đóng gói các quy tắc giữ lại "người dùng", sử dụng cùng định dạng với các quy tắc giữ lại ứng dụng. Các quy tắc này được đóng gói vào cấu phần phần mềm thư viện (AAR hoặc JAR) và được sử dụng tự động trong quá trình tối ưu hoá ứng dụng Android khi thư viện được sử dụng.

Thư viện AAR

Để thêm quy tắc người dùng cho thư viện AAR, hãy sử dụng tuỳ chọn consumerProguardFiles trong tập lệnh bản dựng của mô-đun thư viện Android. Để biết thêm thông tin, hãy xem hướng dẫn tạo mô-đun thư viện.

Kotlin

android {
    defaultConfig {
        consumerProguardFiles("consumer-proguard-rules.pro")
    }
    ...
}

Groovy

android {
    defaultConfig {
        consumerProguardFiles 'consumer-proguard-rules.pro'
    }
    ...
}

Thư viện JAR

Để gói các quy tắc với thư viện Kotlin/Java được vận chuyển dưới dạng JAR, hãy đặt tệp quy tắc vào thư mục META-INF/proguard/ của tệp JAR cuối cùng, với bất kỳ tên tệp nào. Ví dụ: nếu mã của bạn trong <libraryroot>/src/main/kotlin, hãy đặt tệp quy tắc của người dùng tại <libraryroot>/src/main/resources/META-INF/proguard/consumer-proguard-rules.pro và các quy tắc sẽ được đóng gói ở đúng vị trí trong tệp JAR đầu ra.

Xác minh rằng tệp JAR cuối cùng đã gói các quy tắc đúng cách bằng cách kiểm tra để đảm bảo các quy tắc đó nằm trong thư mục META-INF/proguard.

Tối ưu hoá bản dựng thư viện AAR (nâng cao)

Nhìn chung, bạn không nên trực tiếp tối ưu hoá bản dựng thư viện vì các hoạt động tối ưu hoá có thể thực hiện tại thời điểm tạo bản dựng thư viện rất hạn chế. Chỉ trong quá trình tạo bản dựng ứng dụng, khi một thư viện được đưa vào ứng dụng, R8 mới có thể biết cách sử dụng tất cả phương thức của thư viện và thông số nào được truyền. Là nhà phát triển thư viện, bạn cần suy luận về nhiều giai đoạn tối ưu hoá và duy trì hành vi, cả tại thời điểm tạo thư viện và ứng dụng, trước khi tối ưu hoá thư viện đó.

Nếu bạn vẫn muốn tối ưu hoá thư viện của mình tại thời điểm tạo bản dựng, thì Trình bổ trợ Android cho Gradle sẽ hỗ trợ việc này.

Kotlin

android {
    buildTypes {
        release {
            isMinifyEnabled = true
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
        configureEach {
            consumerProguardFiles("consumer-rules.pro")
        }
    }
}

Groovy

android {
    buildTypes {
        release {
            minifyEnabled true
            proguardFiles
                getDefaultProguardFile('proguard-android-optimize.txt'),
                'proguard-rules.pro'
        }
        configureEach {
            consumerProguardFiles "consumer-rules.pro"
        }
    }
}

Xin lưu ý rằng hành vi của proguardFiles rất khác với consumerProguardFiles:

  • proguardFiles được dùng tại thời điểm tạo bản dựng, thường cùng với getDefaultProguardFile("proguard-android-optimize.txt"), để xác định phần nào của thư viện sẽ được giữ lại trong quá trình tạo bản dựng thư viện. Ít nhất, đây là API công khai của bạn.
  • Ngược lại, consumerProguardFiles được đóng gói vào thư viện để ảnh hưởng đến những hoạt động tối ưu hoá diễn ra sau đó, trong quá trình xây dựng một ứng dụng sử dụng thư viện của bạn.

Ví dụ: nếu thư viện của bạn sử dụng tính năng phản chiếu để tạo các lớp nội bộ, bạn có thể cần xác định các quy tắc giữ lại trong cả proguardFilesconsumerProguardFiles.

Nếu bạn sử dụng -repackageclasses trong bản dựng của thư viện, hãy đóng gói lại các lớp vào một gói con bên trong gói của thư viện. Ví dụ: sử dụng -repackageclasses 'com.example.mylibrary.internal' thay vì -repackageclasses 'internal'.

Hỗ trợ nhiều phiên bản R8 (nâng cao)

Bạn có thể điều chỉnh các quy tắc để nhắm đến các phiên bản R8 cụ thể. Điều này cho phép thư viện hoạt động tối ưu trong các dự án sử dụng phiên bản R8 mới hơn, đồng thời cho phép tiếp tục sử dụng các quy tắc hiện có trong các dự án có phiên bản R8 cũ hơn.

Để chỉ định các quy tắc R8 được nhắm mục tiêu, bạn cần đưa các quy tắc đó vào thư mục META-INF/com.android.tools bên trong classes.jar của tệp AAR hoặc trong thư mục META-INF/com.android.tools của tệp JAR.

In an AAR library:
    proguard.txt (legacy location, the file name must be "proguard.txt")
    classes.jar
    └── META-INF
        └── com.android.tools (location of targeted R8 rules)
            ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
            └── ... (more directories with the same name format)

In a JAR library:
    META-INF
    ├── proguard/<ProGuard-rule-files> (legacy location)
    └── com.android.tools (location of targeted R8 rules)
        ├── r8-from-<X>-upto-<Y>/<R8-rule-files>
        └── ... (more directories with the same name format)

Trong thư mục META-INF/com.android.tools, có thể có nhiều thư mục con có tên ở dạng r8-from-<X>-upto-<Y> để cho biết các quy tắc được viết cho phiên bản R8 nào. Mỗi thư mục con có thể có một hoặc nhiều tệp chứa các quy tắc R8, với bất kỳ tên tệp và đuôi tệp nào.

Xin lưu ý rằng các phần -from-<X>-upto-<Y> là không bắt buộc, phiên bản <Y>riêng biệt và các dải phiên bản thường liên tục nhưng cũng có thể trùng lặp.

Ví dụ: r8, r8-upto-8.0.0, r8-from-8.0.0-upto-8.2.0r8-from-8.2.0 là tên thư mục đại diện cho một nhóm quy tắc R8 được nhắm mục tiêu. Mọi phiên bản R8 đều có thể sử dụng các quy tắc trong thư mục r8. R8 có thể sử dụng các quy tắc trong thư mục r8-from-8.0.0-upto-8.2.0 từ phiên bản 8.0.0 trở lên, nhưng không bao gồm phiên bản 8.2.0.

Trình bổ trợ Android cho Gradle sử dụng thông tin đó để chọn tất cả các quy tắc mà phiên bản R8 hiện tại có thể sử dụng. Nếu một thư viện không chỉ định các quy tắc R8 được nhắm mục tiêu, thì trình bổ trợ Android cho Gradle sẽ chọn các quy tắc từ các vị trí cũ (proguard.txt cho AAR hoặc META-INF/proguard/<ProGuard-rule-files> cho JAR).