Tổng quan về bản dựng Gradle

Các ứng dụng Android thường được tạo bằng hệ thống xây dựng Gradle. Trước khi đi sâu vào chi tiết về cách định cấu hình bản dựng, chúng ta sẽ khám phá các khái niệm đằng sau bản dựng để bạn có thể xem hệ thống nói chung.

Bản dựng là gì?

Hệ thống xây dựng sẽ chuyển đổi mã nguồn của bạn thành một ứng dụng có thể thực thi. Các bản dựng thường liên quan đến nhiều công cụ để phân tích, biên dịch, liên kết và đóng gói ứng dụng hoặc thư viện của bạn. Gradle sử dụng phương pháp dựa trên tác vụ để sắp xếp và chạy các lệnh này.

Tác vụ bao gồm các lệnh chuyển đổi dữ liệu đầu vào thành dữ liệu đầu ra. Trình bổ trợ xác định các tác vụ và cấu hình của chúng. Việc áp dụng một trình bổ trợ cho bản dựng sẽ đăng ký các tác vụ của trình bổ trợ đó và kết nối các tác vụ với nhau bằng cách sử dụng dữ liệu đầu vào và đầu ra của chúng. Ví dụ: việc áp dụng Trình bổ trợ Android cho Gradle (AGP) vào tệp bản dựng sẽ đăng ký tất cả các tác vụ cần thiết để tạo một APK hoặc Thư viện Android. Trình bổ trợ java-library cho phép bạn tạo một tệp jar từ mã nguồn Java. Các trình bổ trợ tương tự cũng có trong Kotlin và các ngôn ngữ khác, nhưng các trình bổ trợ khác được dùng để mở rộng trình bổ trợ. Ví dụ: trình bổ trợ protobuf dùng để thêm tính năng hỗ trợ protobuf vào các trình bổ trợ hiện có như AGP hoặc java-library.

Gradle ưu tiên quy ước hơn cấu hình, vì vậy, các trình bổ trợ sẽ đi kèm với các giá trị mặc định phù hợp ngay khi xuất xưởng, nhưng bạn có thể định cấu hình thêm bản dựng thông qua Ngôn ngữ dành riêng cho miền (DSL) khai báo. DSL được thiết kế để bạn có thể chỉ định nội dung cần tạo thay vì cách tạo nội dung đó. Logic trong các trình bổ trợ sẽ quản lý "cách thức". Cấu hình đó được chỉ định trên một số tệp bản dựng trong dự án (và các dự án con).

Đầu vào của tác vụ có thể là tệp và thư mục cũng như các thông tin khác được mã hoá dưới dạng các loại Java (số nguyên, chuỗi hoặc các lớp tuỳ chỉnh). Đầu ra chỉ có thể là thư mục hoặc tệp vì chúng phải được ghi vào đĩa. Việc kết nối đầu ra của một tác vụ với đầu vào của một tác vụ khác sẽ liên kết các tác vụ với nhau để một tác vụ phải chạy trước tác vụ kia.

Mặc dù Gradle hỗ trợ việc viết mã tuỳ ý và khai báo tác vụ trong các tệp bản dựng, nhưng điều này có thể khiến công cụ khó hiểu bản dựng của bạn hơn và bạn khó duy trì hơn. Ví dụ: bạn có thể viết các bài kiểm thử cho mã bên trong các trình bổ trợ nhưng không phải trong các tệp bản dựng. Thay vào đó, bạn nên hạn chế logic xây dựng và khai báo tác vụ đối với các trình bổ trợ (do bạn hoặc người khác xác định) và khai báo cách bạn muốn sử dụng logic đó trong các tệp xây dựng.

Điều gì xảy ra khi một bản dựng Gradle chạy?

Các bản dựng Gradle chạy theo 3 giai đoạn. Mỗi giai đoạn này thực thi các phần mã khác nhau mà bạn xác định trong tệp bản dựng.

  • Khởi chạy xác định những dự án và dự án con được đưa vào bản dựng, đồng thời thiết lập các đường dẫn lớp chứa tệp bản dựng và các trình bổ trợ đã áp dụng. Giai đoạn này tập trung vào một tệp cài đặt nơi bạn khai báo các dự án cần tạo và vị trí để tìm nạp các trình bổ trợ và thư viện.
  • Cấu hình đăng ký các tác vụ cho từng dự án và thực thi tệp bản dựng để áp dụng quy cách bản dựng của người dùng. Điều quan trọng cần hiểu là mã cấu hình của bạn sẽ không có quyền truy cập vào dữ liệu hoặc tệp được tạo trong quá trình thực thi.
  • Execution (Thực thi) thực hiện việc "tạo" ứng dụng thực tế. Đầu ra của cấu hình là một Đồ thị có hướng không chu trình (DAG) của các tác vụ, biểu thị tất cả các bước xây dựng bắt buộc do người dùng yêu cầu (các tác vụ được cung cấp trên dòng lệnh hoặc dưới dạng mặc định trong các tệp bản dựng). Biểu đồ này thể hiện mối quan hệ giữa các tác vụ, có thể là mối quan hệ rõ ràng trong khai báo của một tác vụ hoặc dựa trên các đầu vào và đầu ra của tác vụ đó. Nếu một tác vụ có đầu vào là đầu ra của một tác vụ khác, thì tác vụ đó phải chạy sau tác vụ kia. Giai đoạn này chạy các tác vụ đã lỗi thời theo thứ tự được xác định trong biểu đồ; nếu dữ liệu đầu vào của một tác vụ không thay đổi kể từ lần thực thi gần đây nhất, Gradle sẽ bỏ qua tác vụ đó.

Để biết thêm thông tin, hãy xem Vòng đời bản dựng của Gradle.

DSL cấu hình

Gradle sử dụng Ngôn ngữ dành riêng cho miền (DSL) để định cấu hình các bản dựng. Phương pháp khai báo này tập trung vào việc chỉ định dữ liệu của bạn thay vì viết hướng dẫn từng bước (bắt buộc). Bạn có thể viết tệp bản dựng bằng Kotlin hoặc Groovy, nhưng bạn nên dùng Kotlin.

DSL cố gắng giúp mọi người (cả chuyên gia về miền và lập trình viên) dễ dàng đóng góp cho một dự án, xác định một ngôn ngữ nhỏ đại diện cho dữ liệu theo cách tự nhiên hơn. Các trình bổ trợ Gradle có thể mở rộng DSL để định cấu hình dữ liệu cần thiết cho các tác vụ của chúng.

Ví dụ: việc định cấu hình phần Android của bản dựng có thể trông như sau:

Kotlin

android {
    namespace = "com.example.app"
    compileSdk {
        version = release(36) {
            minorApiLevel = 1
        }
    }
    // ...

    defaultConfig {
        applicationId = "com.example.app"
        minSdk {
            version = release(23)
        }
        targetSdk {
            version = release(36)
        }
        // ...
    }
}

Groovy

android {
    namespace = 'com.example.app'
    compileSdk {
        version = release(36) {
            minorApiLevel = 1
        }
    }
    // ...

    defaultConfig {
        applicationId = 'com.example.app'
        minSdk {
            version = release(23)
        }
        targetSdk {
            version = release(36)
        }
        // ...
    }
}

Trong hậu trường, mã DSL sẽ tương tự như sau:

fun Project.android(configure: ApplicationExtension.() -> Unit) {
    ...
}

interface ApplicationExtension {
    var namespace: String?

    fun compileSdk(configure: CompileSdkSpec.() -> Unit) {
        ...
    }

    val defaultConfig: DefaultConfig

    fun defaultConfig(configure: DefaultConfig.() -> Unit) {
        ...
    }
}

Mỗi khối trong DSL được biểu thị bằng một hàm nhận một lambda để định cấu hình khối đó và một thuộc tính có cùng tên để truy cập vào khối đó. Điều này khiến mã trong tệp bản dựng của bạn giống như một quy cách dữ liệu hơn.

Phần phụ thuộc bên ngoài

Hệ thống xây dựng Maven đã giới thiệu một quy cách phần phụ thuộc, hệ thống lưu trữ và quản lý. Các thư viện được lưu trữ trong kho lưu trữ (máy chủ hoặc thư mục), cùng với siêu dữ liệu bao gồm phiên bản và các phần phụ thuộc của chúng vào các thư viện khác. Bạn chỉ định kho lưu trữ cần tìm kiếm, phiên bản của các phần phụ thuộc mà bạn muốn sử dụng và hệ thống bản dựng sẽ tải các phần phụ thuộc đó xuống trong quá trình tạo bản dựng.

Cấu phần phần mềm Maven được xác định bằng tên nhóm (công ty, nhà phát triển, v.v.), tên cấu phần phần mềm (tên của thư viện) và phiên bản của cấu phần phần mềm đó. Thường được biểu thị dưới dạng group:artifact:version.

Phương pháp này giúp cải thiện đáng kể việc quản lý bản dựng. Bạn thường nghe thấy các kho lưu trữ như vậy được gọi là "kho lưu trữ Maven", nhưng tất cả đều liên quan đến cách các cấu phần phần mềm được đóng gói và xuất bản. Các kho lưu trữ và siêu dữ liệu này đã được dùng lại trong một số hệ thống bản dựng, bao gồm cả Gradle (và Gradle có thể xuất bản lên các kho lưu trữ này). Kho lưu trữ công khai cho phép mọi người chia sẻ và sử dụng, còn kho lưu trữ của công ty sẽ giữ các phần phụ thuộc nội bộ trong nội bộ.

Bạn cũng có thể phân chia dự án thành các dự án con (còn gọi là "mô-đun" trong Android Studio), cũng có thể dùng làm các phần phụ thuộc. Mỗi dự án con tạo ra các đầu ra (chẳng hạn như jar) mà các dự án con hoặc dự án cấp cao nhất của bạn có thể sử dụng. Điều này có thể cải thiện thời gian tạo bằng cách tách biệt những phần cần được tạo lại, cũng như tách biệt tốt hơn các trách nhiệm trong ứng dụng.

Chúng ta sẽ tìm hiểu chi tiết hơn về cách chỉ định các phần phụ thuộc trong bài viết Thêm phần phụ thuộc cho bản dựng.

Biến thể bản dựng

Khi tạo một ứng dụng Android, bạn thường muốn tạo nhiều biến thể. Các biến thể chứa mã khác nhau hoặc được tạo bằng các lựa chọn khác nhau, đồng thời bao gồm các loại bản dựng và phiên bản sản phẩm.

Loại bản dựng có nhiều lựa chọn bản dựng được khai báo. Theo mặc định, AGP thiết lập các loại bản dựng "release" và "debug", nhưng bạn có thể điều chỉnh và thêm các loại bản dựng khác (có thể là để dàn dựng hoặc kiểm thử nội bộ).

Bản gỡ lỗi không giảm thiểu hoặc làm rối mã nguồn ứng dụng của bạn, giúp tăng tốc quá trình tạo bản gỡ lỗi và giữ nguyên tất cả các biểu tượng. Bản gỡ lỗi cũng đánh dấu ứng dụng là "có thể gỡ lỗi", ký ứng dụng bằng khoá gỡ lỗi chung và cho phép truy cập vào các tệp ứng dụng đã cài đặt trên thiết bị. Nhờ đó, bạn có thể khám phá dữ liệu đã lưu trong các tệp và cơ sở dữ liệu trong khi chạy ứng dụng.

Bản phát hành tối ưu hoá ứng dụng, ký ứng dụng bằng khoá phát hành và bảo vệ các tệp ứng dụng đã cài đặt.

Bằng cách sử dụng các phiên bản sản phẩm, bạn có thể thay đổi nguồn được đưa vào và các biến thể phần phụ thuộc cho ứng dụng. Ví dụ: bạn có thể muốn tạo các phiên bản "demo" và "full" cho ứng dụng của mình, hoặc có thể là các phiên bản "free" và "paid". Bạn viết nguồn chung trong thư mục "main" source set (nhóm tài nguyên), rồi ghi đè hoặc thêm nguồn vào một nhóm tài nguyên được đặt tên theo phiên bản.

AGP tạo các biến thể cho mỗi tổ hợp loại bản dựng và phiên bản sản phẩm. Nếu bạn không xác định phiên bản, các biến thể sẽ được đặt tên theo loại bản dựng. Nếu bạn xác định cả hai, biến thể sẽ có tên là <flavor><Buildtype>. Ví dụ: với các loại bản dựng releasedebug, cùng với các phiên bản demofull, AGP sẽ tạo các biến thể:

  • demoRelease
  • demoDebug
  • fullRelease
  • fullDebug

Các bước tiếp theo

Giờ đây, khi đã nắm được các khái niệm về bản dựng, hãy xem cấu trúc bản dựng Android trong dự án của bạn.