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

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

Công trình 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. 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. 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ụ đóng gói các lệnh chuyển dữ liệu đầu vào thành đầu ra. Trình bổ trợ xác định các nhiệm vụ và cấu hình của chúng. Việc áp dụng 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 các tác vụ đó. 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ý mọi thao tác cần thiết để tạo APK hoặc Thư viện Android. Trình bổ trợ java-library cho phép bạn tạo một jar từ mã nguồn Java. Có các trình bổ trợ tương tự cho Kotlin và các ngôn ngữ khác, nhưng các trình bổ trợ khác là để 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 cho 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 từ đầu. Tuy nhiên, bạn có thể định cấu hình thêm cho bản dựng thông qua một 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 xây dựng thay vì cách tạo. Logic trong các trình bổ trợ sẽ quản lý "cách". Cấu hình đó được chỉ định trên một số tệp bản dựng trong dự án (và dự án phụ).

Dữ liệu đầ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 loại Java (số nguyên, chuỗi hoặ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 trên ổ đĩa. Khi kết nối đầu ra tác vụ với đầu vào của tác vụ khác, hãy 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ết mã tuỳ ý và nội dung khai báo tác vụ trong các tệp bản dựng của bạn, nhưng điều này có thể khiến công cụ gặp khó khăn trong việc hiểu bản dựng của bạn và cho việc bảo trì. Ví dụ: bạn có thể viết các chương trình kiểm thử cho mã bên trong trình bổ trợ nhưng không viết được trong tệp bản dựng. Thay vào đó, bạn nên hạn chế phần khai báo logic của bản dựng và tác vụ đối với các trình bổ trợ (mà bạn hoặc người khác xác định), đồng thời khai báo cách bạn muốn sử dụng logic đó trong các tệp bản dựng.

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

Bản dựng Gradle chạy theo ba 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 phụ nào được đưa vào bản dựng, đồng thời thiết lập classpath chứa các tệp bản dựng và trình bổ trợ áp dụng. Giai đoạn này tập trung vào 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 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 thông số kỹ thuật bản dựng của người dùng. Điều quan trọng là bạn phải hiểu rằng mã cấu hình 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.
  • Quá trình thực thi thực hiện quá trình "xây dựng" thực tế của ứng dụng. Đầu ra của cấu hình là một Biểu đồ không chu trình có hướng (DAG) của các tác vụ, thể hiện tất cả các bước tạo bản dựng bắt buộc mà 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 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à rõ ràng trong phần khai báo của một tác vụ, hoặc dựa trên dữ liệu đầ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ụ khác. 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 một 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 các 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 sử dụng Kotlin.

DSL cố gắng giúp mọi người, từ chuyên gia trong lĩnh vực đến lập trình viên, dễ dàng đóng góp vào 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. 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ụ: định cấu hình phần Android của bản dựng có thể có dạng như sau:

Kotlin

android {
    namespace = "com.example.app"
    compileSdk = 34
    // ...

    defaultConfig {
        applicationId = "com.example.app"
        minSdk = 34
        // ...
    }
}

Groovy

android {
    namespace 'com.example.myapplication'
    compileSdk 34
    // ...

    defaultConfig {
        applicationId "com.example.myapplication"
        minSdk 24
        // ...
    }
}

Mã DSL tương tự như sau:

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

interface ApplicationExtension {
    var compileSdk: Int
    var namespace: String?

    val defaultConfig: DefaultConfig

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

Mỗi khối trong DSL được biểu thị bằng một hàm lấy một lambda để định cấu hình và một thuộc tính có cùng tên để truy cập vào hàm đó. Điều này khiến mã trong tệp bản dựng giống với mã thông số kỹ thuật 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 thông số kỹ thuật, hệ thống lưu trữ và quản lý phần phụ thuộc. Thư viện được lưu trữ trong kho lưu trữ (máy chủ hoặc thư mục), với siêu dữ liệu bao gồm phiên bản và các phần phụ thuộc trên các thư viện khác. Bạn sẽ chỉ định những kho lưu trữ cần tìm kiếm, các phiên bản của phần phụ thuộc mà bạn muốn sử dụng và hệ thống xây dựng sẽ tải các kho lưu trữ đó 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 theo 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 đó. Thuộc tính này thường được biểu thị là 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 đó chỉ là cách đóng gói và phát hành cấu phần phần mềm. Các kho lưu trữ và siêu dữ liệu này đã được sử dụng lại trong một số hệ thống xây dựng, bao gồm cả Gradle (và Gradle có thể phát hành vào các kho lưu trữ này). Kho lưu trữ công khai cho phép chia sẻ để mọi người sử dụng, còn kho lưu trữ của công ty lưu trữ các phần phụ thuộc nội bộ nội bộ.

Bạn cũng có thể mô-đun hoá dự án thành dự án phụ (còn gọi là "mô-đun" trong Android Studio). Các dự án phụ này cũng có thể được dùng làm phần phụ thuộc. Mỗi dự án phụ tạo ra các đầu ra (chẳng hạn như tệp jar) mà các dự án phụ 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 xây dựng bằng cách tách biệt những phần cần xây dựng 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 phần phụ thuộc trong phần Thêm phần phụ thuộc của 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 xây dựng bằng nhiều tuỳ chọn, đồ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 khác nhau về tuỳ chọn bản dựng đã khai báo. Theo mặc định, AGP sẽ thiết lập các loại bản dựng "phát hành" và "gỡ lỗi", nhưng bạn có thể điều chỉnh các loại bản dựng này và thêm các loại bản dựng khác (có thể để thử nghiệm hoặc kiểm thử nội bộ).

Bản dựng gỡ lỗi không rút gọn hoặc làm rối mã nguồn của ứng dụng, giúp tăng tốc bản dựng và giữ nguyên tất cả các biểu tượng. Bản dựng này 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ị. Điều này cho phép bạn 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.

Khi sử dụng phiên bản sản phẩm, bạn có thể thay đổi nguồn và các biến thể phần phụ thuộc đi kèm cho ứng dụng. Ví dụ: bạn có thể muốn tạo phiên bản "minh hoạ" và "đầy đủ" cho ứng dụng của mình, hoặc có thể là phiên bản "miễn phí" và "có tính phí". Bạn viết nguồn chung trong thư mục nhóm tài nguyên "chính" và ghi đè hoặc thêm nguồn vào nhóm tài nguyên được đặt tên theo hương vị.

AGP tạo ra các biến thể cho từng 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 tên là <flavor><Buildtype>. Ví dụ: với các loại bản dựng releasedebug cũng như 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ờ bạn đã thấy 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.