Tối ưu hoá tốc độ bản dựng

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Thời gian tạo bản dựng dài sẽ làm chậm quá trình phát triển. Trang này cung cấp một số kỹ thuật giúp tháo gỡ khó khăn liên quan đến tốc độ tạo bản dựng.

Dưới đây là quy trình chung để cải thiện tốc độ bản dựng:

  1. Tối ưu hóa cấu hình bản dựng bằng cách thực hiện một số bước để mang lại lợi ích tức thì cho hầu hết các dự án Android Studio.
  2. Tạo hồ sơ bản dựng để xác định và tháo gỡ một vài khó khăn đặc thù đối với dự án hoặc máy trạm của bạn.

Khi phát triển ứng dụng, bạn nên triển khai cho thiết bị chạy Android từ 7.0 (API cấp 24) trở lên bất cứ khi nào có thể. Các phiên bản nền tảng Android mới sẽ triển khai cơ chế hiệu quả hơn để tạo bản cập nhật cho ứng dụng, chẳng hạn như Android Runtime (ART) và dịch vụ hỗ trợ gốc cho nhiều tệp DEX.

Lưu ý: Sau bản dựng sạch đầu tiên, bạn có thể nhận thấy rằng các bản dựng tiếp theo — các bản dựng sạch và tăng dần (clean and incremental builds) — hoạt động nhanh hơn nhiều (ngay cả khi không sử dụng bất kỳ tính năng tối ưu hóa nào được mô tả tại trang này). Điều này là nhờ trình nền Gradle có giai đoạn khởi động hiệu suất gia tăng—tương tự như các quy trình JVM khác.

Tối ưu hoá cấu hình bản dựng

Tham khảo các mẹo dưới đây để cải thiện tốc độ tạo bản dựng cho dự án Android Studio.

Luôn cập nhật các công cụ của bạn

Các công cụ Android sẽ có thêm tính năng mới và khả năng tối ưu hoá bản dựng sau hầu hết mọi lần cập nhật. Một số mẹo tại trang này chỉ áp dụng nếu bạn đang dùng phiên bản mới nhất. Để tận dụng tối đa các tính năng tối ưu hoá mới nhất, hãy cập nhật các nội dung sau:

Tránh biên dịch các tài nguyên không cần thiết

Tránh biên dịch và đóng gói tài nguyên bạn không thử nghiệm (chẳng hạn như các tài nguyên bản địa hóa ngôn ngữ bổ sung hay tài nguyên mật độ màn hình). Thay vào đó, hãy chỉ xác định một tài nguyên ngôn ngữ và mật độ màn hình cho phiên bản "dev" của bạn, như minh họa dưới đây:

Groovy

android {
    ...
    productFlavors {
        dev {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations "en", "xxhdpi"
        }
        ...
    }
}

Kotlin

android {
    ...
    productFlavors {
        create("dev") {
            ...
            // The following configuration limits the "dev" flavor to using
            // English stringresources and xxhdpi screen-density resources.
            resourceConfigurations("en", "xxhdpi")
        }
        ...
    }
}

Thử nghiệm với việc đặt Cổng trình bổ trợ Gradle ở cuối

Trong Android, tất cả trình bổ trợ đều có trong kho lưu trữ google()mavenCentral(). Tuy nhiên, bản dựng có thể cần các trình bổ trợ của bên thứ ba được phân giải bằng dịch vụ gradlePluginPortal().

Gradle tìm kiếm kho lưu trữ theo thứ tự khai báo, vậy nên hiệu suất tạo bản dựng sẽ được cải thiện nếu bạn liệt kê các kho lưu trữ chứa nhiều trình bổ trợ nhất trước tiên. Do đó, hãy thử nghiệm mục gradlePluginPortal() bằng cách đặt mục đó ở cuối của khối lưu trữ trong tệp settings.gradle. Trong hầu hết các trường hợp, điều này sẽ giúp giảm thiểu số lượt tìm kiếm trình bổ trợ không cần thiết và cải thiện tốc độ tạo bản dựng.

Để biết thêm thông tin về cách Gradle khám phá nhiều kho lưu trữ, hãy xem phần Khai báo nhiều kho lưu trữ trong tài liệu về Gradle.

Dùng các giá trị cấu hình bản dựng tĩnh cho bản gỡ lỗi

Luôn dùng giá trị tĩnh cho các thuộc tính có trong tệp kê khai hoặc tệp tài nguyên cho bản gỡ lỗi.

Việc sử dụng mã phiên bản động, tên phiên bản, tài nguyên hoặc bất kỳ logic tạo bản dựng nào khác làm thay đổi tệp kê khai sẽ yêu cầu một bản dựng đầy đủ cho ứng dụng mỗi khi bạn muốn chạy thay đổi – ngay cả khi thay đổi thực tế đáng ra chỉ yêu cầu hoán đổi nóng. Nếu cấu hình bản dựng của bạn yêu cầu các thuộc tính động này, hãy tách riêng các thuộc tính đó thành nhiều biến thể của bản phát hành và giữ lại các giá trị tĩnh cho bản gỡ lỗi của bạn.

Hãy xem ví dụ sau đây trong tệp build.gradle:

Groovy

int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        // Making either of these two values dynamic in the defaultConfig will
        // require a full app build and reinstallation, because the AndroidManifest.xml
        // must be updated.
        versionCode 1
        versionName "1.0"
        ...
    }

    // The defaultConfig values above are fixed, so your incremental builds don't
    // need to rebuild the manifest.
    // For release builds, it's OK. The following script iterates through
    // all the known variants, finds those that are "release" build types, and
    // changes those properties to something dynamic.
    applicationVariants.all { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch;
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName;
        }
    }
}

Kotlin

val MILLIS_IN_MINUTE = 1000 * 60
val minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE

android {
    ...
    defaultConfig {
        // Making either of these two values dynamic in the defaultConfig will
        // require a full app build and reinstallation, because the AndroidManifest.xml
        // must be updated.
        versionCode = 1
        versionName = "1.0"
        ...
    }

    // The defaultConfig values above are fixed, so your incremental builds don't
    // need to rebuild the manifest.
    // For release builds, it's OK. The following script iterates through
    // all the known variants, finds those that are "release" build types, and
    // changes those properties to something dynamic.
    applicationVariants.forEach { variant ->
        if (variant.buildType.name == "release") {
            variant.mergedFlavor.versionCode = minutesSinceEpoch
            variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName
        }
    }
}

Sử dụng các phiên bản phần phụ thuộc tĩnh

Khi khai báo các phần phụ thuộc trong tệp build.gradle, bạn nên tránh sử dụng số phiên bản động (những số có dấu cộng ở cuối, chẳng hạn như 'com.android.tools.build:gradle:2.+'). Việc sử dụng số phiên bản động có thể dẫn đến tình trạng cập nhật phiên bản không mong muốn, gây khó khăn khi phân biệt các phiên bản, và làm chậm các bản dựng do quá trình kiểm tra Gradle cho các bản cập nhật. Thay vào đó, hãy sử dụng số phiên bản tĩnh.

Tạo các mô-đun thư viện

Tìm mã trong ứng dụng mà bạn có thể chuyển đổi thành mô-đun thư viện Android. Việc mô-đun hóa mã theo cách này cho phép hệ thống tạo bản dựng chỉ biên dịch những mô-đun bạn sửa đổi và lưu kết quả đó vào bộ nhớ đệm cho các bản dựng sau này. Ngoài ra, tính năng này cũng cải thiện hiệu quả của phương thức thực thi dự án song songkhi bạn bật tính năng tối ưu hóa đó.

Tạo nhiệm vụ cho logic bản dựng tuỳ chỉnh

Sau khi bạn tạo hồ sơ bản dựng, nếu hồ sơ bản dựng chỉ ra rằng giai đoạn **Định cấu hình Dự án** chiếm tương đối nhiều thời gian, hãy xem lại tập lệnh build.gradle và tìm mã để đưa vào một nhiệm vụ tùy chỉnh trong Gradle. Khi bạn di chuyển một số logic tạo bản dựng vào một tác vụ, bạn đã đảm bảo rằng tác vụ đó chỉ chạy khi cần thiết và kết quả có thể được lưu vào bộ nhớ đệm để dùng cho các bản dựng sau này, đồng thời, logic tạo bản dựng đó cũng có thể chạy song song nếu bạn bật phương thức thực thi dự án song song. Để tìm hiểu thêm về tác vụ cho logic bản dựng tuỳ chỉnh, hãy đọc tài liệu chính thức về Gradle.

Mẹo: Nếu bản dựng chứa nhiều tác vụ tùy chỉnh thì bạn có thể sắp xếp tệp gọn gàng build.gradle bằng cách tạo lớp tác vụ tùy chỉnh. Hãy thêm các lớp vào thư mục project-root/buildSrc/src/main/groovy/; và Gradle sẽ tự động đưa các lớp này vào classpath cho tất cả tệp build.gradle trong dự án của bạn.

Chuyển đổi hình ảnh sang WebP

WebP là một định dạng tệp hình ảnh cung cấp khả năng nén có tổn hao (như JPEG) cũng như độ trong suốt (như PNG) nhưng có thể có khả năng nén tốt hơn so với JPEG hoặc PNG.

Việc giảm kích thước tệp hình ảnh mà không cần thực hiện thao tác nén tại thời điểm tạo bản dựng có thể giúp tăng tốc độ bản dựng, đặc biệt là khi ứng dụng sử dụng nhiều tài nguyên hình ảnh. Tuy nhiên, bạn có thể thấy mức sử dụng CPU của thiết bị tăng lên đôi chút trong khi nén các hình ảnh WebP. Sử dụng Android Studio để dễ dàng chuyển đổi hình ảnh sang WebP.

Tắt tính năng nén tệp PNG

Nếu không chuyển đổi hình ảnh PNG thành WebP, bạn vẫn có thể tăng tốc độ bản dựng bằng cách tắt tính năng nén hình ảnh tự động mỗi lần tạo ứng dụng.

Nếu bạn đang sử dụng Trình bổ trợ Android cho Gradle 3.0.0 trở lên thì theo mặc định, tính năng nén tệp PNG sẽ chỉ tắt đối với loại bản "gỡ lỗi". Để tắt tính năng tối ưu hóa này đối với các loại bản dựng khác, hãy thêm đoạn mã sau vào tệp build.gradle:

Groovy

android {
    buildTypes {
        release {
            // Disables PNG crunching for the "release" build type.
            crunchPngs false
        }
    }
}

Kotlin

android {
    buildTypes {
        getByName("release") {
            // Disables PNG crunching for the "release" build type.
            isCrunchPngs = false
        }
    }
}

Vì các loại bản dựng hay phiên bản sản phẩm không xác định thuộc tính này, nên bạn cần thiết lập thuộc tính này theo cách thủ công thành true khi tạo phiên bản phát hành cho ứng dụng.

Thử nghiệm với trình thu thập rác song song của JVM

Bạn có thể cải thiện hiệu suất bản dựng bằng cách định cấu hình trình thu thập rác tối ưu của máy ảo mà Gradle sử dụng. JDK 8 được định cấu hình để sử dụng trình thu thập rác song song theo mặc định và JDK 9 trở lên được định cấu hình để sử dụng trình thu thập rác G1.

Để có thể cải thiện hiệu suất bản dựng, bạn nên kiểm thử bản dựng Gradle bằng trình thu thập rác song song. Trong gradle.properties, hãy thiết lập các điểm sau:

org.gradle.jvmargs=-XX:+UseParallelGC

Nếu trường này đã có các tuỳ chọn khác, hãy thêm tuỳ chọn mới:

org.gradle.jvmargs=-Xmx1536m -XX:+UseParallelGC

Để đo tốc độ bản dựng với các cấu hình khác nhau, hãy xem nội dung Phân tích bản dựng.

Tăng kích thước vùng nhớ xếp JVM

Nếu bạn nhận thấy các bản dựng có tốc độ chậm và cụ thể là việc thu thập rác chiếm hơn 15% thời gian dựng trong kết quả Trình phân tích bản dựng, bạn nên tăng Kích thước vùng nhớ khối xếp của Máy ảo Java (JVM). Trong tệp gradle.properties, hãy đặt giới hạn là 4, 6 hoặc 8 gigabyte như trong ví dụ sau:

org.gradle.jvmargs=-Xmx6g

Sau đó, hãy kiểm thử để kiểm tra tốc độ bản dựng. Cách dễ nhất để xác định kích thước vùng nhớ khối xếp tối ưu là tăng giới hạn lên một chút, sau đó kiểm thử để xem mức cải thiện tốc độ bản dựng cần thiết.

Nếu bạn cũng sử dụng Trình thu thập rác song song JVM, cả dòng sẽ có định dạng như sau:

org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g

Bạn có thể phân tích lỗi bộ nhớ JVM bằng cách bật cờ HeapDumpOnOutOfMemoryError. Sau khi làm như vậy, JVM sẽ tạo ra một tệp báo lỗi khi hết bộ nhớ.

Dùng các lớp R không mang tính chất bắc cầu

Sử dụng các lớp R không mang tính chất bắc cầu để tăng tốc độ bản dựng cho các ứng dụng có nhiều mô-đun. Nhờ vậy, bạn sẽ có thể ngăn chặn tình trạng trùng lặp tài nguyên bằng cách đảm bảo rằng lớp R của mỗi mô-đun chỉ chứa tệp đối chiếu đến tài nguyên riêng của mô-đun đó mà không cần lấy các tệp đối chiếu từ các phần phụ thuộc tương ứng. Điều này giúp tăng tốc độ bản dựng và mang tới nhiều lợi ích tương ứng nhờ việc tránh sử dụng trình biên dịch.

Bắt đầu từ Android Studio Bumblebee, các lớp R không mang tính chất bắc cầu luôn bật theo mặc định cho các dự án mới. Đối với dự án được tạo bằng phiên bản cũ của Android Studio, bạn có thể cập nhật các phiên bản đó để sử dụng lớp R không mang tính chất bắc cầu bằng cách chuyển đến mục Tái cấu trúc > Di chuyển sang Lớp R không mang tính chất bắc cầu.

Để tìm hiểu thêm về tài nguyên ứng dụng và lớp R, vui lòng xem Tổng quan về tài nguyên ứng dụng.

Tắt cờ Jetifier

Vì hầu hết dự án đều sử dụng trực tiếp thư viện AndroidX, bạn có thể tắt cờ Jetifier để cải thiện hiệu suất bản dựng. Để gỡ bỏ cờ Jetifier, hãy đặt android.enableJetifier=false trong tệp gradle.properties.

Trình phân tích bản dựng có thể thực hiện quá trình kiểm tra để xem liệu cờ có thể được gỡ bỏ một cách an toàn hay không nhằm giúp dự án của bạn đạt hiệu suất bản dựng tốt hơn đồng thời di chuyển khỏi các Thư viện hỗ trợ Android không còn duy trì. Để tìm hiểu thêm về Trình phân tích bản dựng, vui lòng xem bài viết Khắc phục sự cố về hiệu suất bản dựng.

Sử dụng tính năng lưu cấu hình vào bộ nhớ đệm (thử nghiệm)

Lưu cấu hình vào bộ nhớ đệm là một tính năng thử nghiệm cho phép Gradle ghi lại thông tin về biểu đồ tác vụ của bản dựng và sử dụng lại thông tin đó trong các bản dựng sau này. Tính năng này giúp Gradle không phải định cấu hình lại cho toàn bộ bản dựng.

Để bật tính năng lưu cấu hình vào bộ nhớ đệm, hãy làm theo các bước sau:

  1. Kiểm tra để đảm bảo tất cả trình bổ trợ của dự án đều tương thích.

    Sử dụng Trình phân tích bản dựng để kiểm tra xem dự án của bạn có tương thích với tính năng lưu cấu hình vào bộ nhớ đệm hay không. Trình phân tích bản dựng sẽ chạy một loạt bản thử nghiệm để xác định xem tính năng này có sử dụng được cho dự án hay không. Hãy xem vấn đề #13490 để biết danh sách các trình bổ trợ được hỗ trợ.

  2. Thêm mã sau vào tệp gradle.properties:

      org.gradle.unsafe.configuration-cache=true
      # Use this flag carefully, in case some of the plugins are not fully compatible.
      org.gradle.unsafe.configuration-cache-problems=warn

Khi bật tính năng lưu cấu hình vào bộ nhớ đệm, kết quả của bản dựng sẽ là Calculating task graph as no configuration cache is available for tasks khi bạn khởi chạy dự án lần đầu. Trong các lần chạy tiếp theo, kết quả bản dựng sẽ là Reusing configuration cache.

Để tìm hiểu thêm về tính năng lưu cấu hình vào bộ nhớ đệm, hãy xem bài đăng trên blog Tìm hiểu chuyên sâu về bộ nhớ đệm của cấu hình và tài liệu của Gradle về lưu cấu hình vào bộ nhớ đệm.