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

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 đưa ra một số kỹ thuật giúp tháo gỡ nút thắt cổ chai 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 hoá 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à chẩn đoán một vài nút thắt cổ chai đặ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 (clean build) đầ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 — hoạt động nhanh hơn nhiều (ngay cả khi không sử dụng bất cứ tính năng tối ưu hoá 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

Hãy làm theo 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 kiểm thử (ví dụ: tài nguyên bản địa hoá 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 hoạ 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 mọi logic tạo bản dựng khác làm thay đổi tệp kê khai sẽ đòi hỏi 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 (hot swap). Nếu cấu hình bản dựng của bạn đòi hỏi những thuộc tính động như vậ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, như trong ví dụ sau:

  ...
  // Use a filter to apply onVariants() to a subset of the variants.
  onVariants(selector().withBuildType("release")) { variant ->
      // Because an app module can have multiple outputs when using multi-APK, versionCode
      // is only available on the variant output.
      // Gather the output when we are in single mode and there is no multi-APK.
      val mainOutput = variant.outputs.single { it.outputType == OutputType.SINGLE }

      // Create the version code generating task.
      val versionCodeTask = project.tasks.register("computeVersionCodeFor${variant.name}", VersionCodeTask::class.java) {
          it.outputFile.set(project.layout.buildDirectory.file("versionCode${variant.name}.txt"))
      }

      // Wire the version code from the task output.
      // map will create a lazy Provider that:
      // 1. Runs just before the consumer(s), ensuring that the producer (VersionCodeTask) has run
      //    and therefore the file is created.
      // 2. Contains task dependency information so that the consumer(s) run after the producer.
      mainOutput.versionCode.set(versionCodeTask.flatMap { it.outputFile.map { it.asFile.readText().toInt() } })
  }
  ...

  abstract class VersionCodeTask : DefaultTask() {

    @get:OutputFile
    abstract val outputFile: RegularFileProperty

    @TaskAction
    fun action() {
        outputFile.get().asFile.writeText("1.1.1")
    }
  }

Xem công thức setVersionsFromTask trên GitHub để tìm hiểu cách thiết lập mã phiên bản động trong dự án.

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, đồng thời 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 hoá 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 song khi bạn bật tính năng tối ưu hoá đó.

Tạo tác 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 tác vụ Gradle tuỳ chỉnh. Bằng việc di chuyển một số logic 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 dựng đó cũng có thể chạy song song nếu bạn bật chế độ thực thi dự án song song. Để tìm hiểu thêm về tác vụ cho logic 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ụ tuỳ 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ụ tuỳ chỉnh. Hãy thêm các lớp vào thư mục project-root/buildSrc/src/main/groovy/; 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 tính năng nén có tổn hao (như JPEG) cũng như độ trong suốt (như PNG) nhưng có thể đạt đượ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 độ 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 độ tạo bản dựng bằng cách tắt tính năng nén hình ảnh tự động mỗi khi dựng ứ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 hoá 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 Java (JVM) 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 có các tuỳ chọn khác đã được thiết lập trong trường này, hãy thêm một tuỳ chọn mới:

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

Để đo tốc độ tạo bản dựng với nhiều cấu hình, hãy xem phần 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 độ tạo 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 thực hiện, JVM sẽ tạo 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 độ tạo bản dựng cho các ứng dụng có nhiều mô-đun. Nhờ vậy, bạn 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 tham chiếu đến tài nguyên riêng của mô-đun đó mà không cần lấy các tệp tham chiếu từ các phần phụ thuộc tương ứng. Điều này giúp tăng tốc độ tạo bản dựng và mang tới nhiều lợi ích tương ứng nhờ việc tránh phải sử dụng trình biên dịch. Đây là hành vi mặc định trong Trình bổ trợ Android cho Gradle phiên bản 8.0.0 trở lên.

Kể từ Android Studio Bumblebee, theo mặc định thì các lớp R không mang tính chất bắc cầu luôn được bật đối với các dự án mới. Đối với dự án được tạo bằng phiên bản Android Studio cũ, 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 phần Refactor > Migrate to Non-Transitive R Classes (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.

Dùng các lớp R không cố định

Sử dụng các trường lớp R không cố định trong ứng dụng và hoạt động kiểm thử để cải thiện mức độ gia tăng trong quá trình biên dịch Java và cho phép rút gọn tài nguyên một cách chính xác hơn. Các trường lớp R luôn không cố định đối với thư viện, vì các tài nguyên được đánh số khi đóng gói APK cho ứng dụng hoặc hoạt động kiểm thử phụ thuộc vào thư viện đó. Đây là hành vi mặc định trong Trình bổ trợ Android cho Gradle phiên bản 8.0.0 trở lên.

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ể gỡ bỏ được 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 nội dung Khắc phục sự cố về hiệu suất dựng.

Sử dụng bộ nhớ đệm cấu hình (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 bộ nhớ đệm cấu hình, 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 bộ nhớ đệm cấu hình hay không. Trình phân tích bản dựng sẽ chạy một loạt bản dựng 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 đoạn mã sau đây 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ộ nhớ đệm cấu hình được bật, trong lần đầu tiên bạn chạy dự án, kết quả bản dựng sẽ là Calculating task graph as no configuration cache is available for tasks. 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ề bộ nhớ đệm cấu hình, hãy xem bài đăng Tìm hiểu chuyên sâu về bộ nhớ đệm cấu hình trên blog và tài liệu của Gradle về bộ nhớ đệm cấu hình.