ภาพรวมของบิลด์ Gradle

โดยปกติแล้ว แอปพลิเคชัน Android จะสร้างขึ้นโดยใช้ระบบบิลด์ Gradle ก่อนที่จะเจาะลึกรายละเอียดเกี่ยวกับวิธีกำหนดค่าบิลด์ เราจะสำรวจแนวคิดเบื้องหลังบิลด์เพื่อให้คุณเห็นภาพรวมของระบบ

บิลด์คืออะไร

ระบบบิลด์จะเปลี่ยนรูปแบบซอร์สโค้ดให้เป็นแอปพลิเคชันที่เรียกใช้ได้ การบิลด์มักเกี่ยวข้องกับเครื่องมือหลายอย่างเพื่อวิเคราะห์ คอมไพล์ ลิงก์ และแพ็กเกจแอปพลิเคชันหรือไลบรารี Gradle ใช้แนวทางที่อิงตามงานเพื่อจัดระเบียบและเรียกใช้คำสั่งเหล่านี้

Tasks จะรวมคำสั่งที่แปลอินพุตเป็นเอาต์พุต ปลั๊กอินจะกำหนดงานและการกำหนดค่างาน การใช้ปลั๊กอินกับบิลด์จะลงทะเบียนงานและเชื่อมโยงงานเหล่านั้นเข้าด้วยกันโดยใช้อินพุตและเอาต์พุต ตัวอย่างเช่น การใช้ปลั๊กอิน Android Gradle (AGP) กับไฟล์บิลด์จะลงทะเบียนงานทั้งหมดที่จำเป็นในการสร้าง APK หรือไลบรารี Android ปลั๊กอิน java-library ช่วยให้คุณสร้าง jar จากซอร์สโค้ด Java ได้ ปลั๊กอินที่คล้ายกันมีให้บริการสำหรับ Kotlin และภาษาอื่นๆ แต่ปลั๊กอินอื่นๆ มีไว้เพื่อขยายปลั๊กอิน ตัวอย่างเช่น ปลั๊กอิน protobuf มีไว้เพื่อเพิ่มการรองรับ protobuf ให้กับปลั๊กอินที่มีอยู่ เช่น AGP หรือ java-library

Gradle จะให้ความสำคัญกับข้อตกลงมากกว่าการกำหนดค่า ดังนั้นปลั๊กอินจึงมาพร้อมกับค่าเริ่มต้นที่ดีตั้งแต่แกะกล่อง แต่คุณสามารถกำหนดค่าบิลด์เพิ่มเติมผ่านภาษาเฉพาะโดเมน (DSL) แบบประกาศได้ DSL ได้รับการออกแบบมาเพื่อให้คุณระบุสิ่งที่จะสร้างได้ แทนที่จะเป็นวิธีสร้าง ตรรกะในปลั๊กอินจะจัดการ "วิธีการ" การกำหนดค่าดังกล่าวจะระบุไว้ในไฟล์บิลด์หลายไฟล์ในโปรเจ็กต์ (และโปรเจ็กต์ย่อย)

อินพุตของงานอาจเป็นไฟล์และไดเรกทอรี รวมถึงข้อมูลอื่นๆ ที่เข้ารหัสเป็นประเภท Java (จำนวนเต็ม สตริง หรือคลาสที่กำหนดเอง) เอาต์พุตต้องเป็นไดเรกทอรีหรือไฟล์เท่านั้น เนื่องจากต้องเขียนลงในดิสก์ การเชื่อมต่อเอาต์พุตของงานเข้ากับอินพุตของงานอื่นจะลิงก์งานเข้าด้วยกันเพื่อให้งานหนึ่งต้องทำงานก่อนอีกงานหนึ่ง

แม้ว่า Gradle จะรองรับการเขียนโค้ดและการประกาศงานที่กำหนดเองในไฟล์บิลด์ แต่ก็อาจทำให้เครื่องมือเข้าใจบิลด์และดูแลรักษาได้ยากขึ้น เช่น คุณสามารถเขียนการทดสอบสำหรับโค้ดภายในปลั๊กอินได้ แต่จะเขียนในไฟล์บิลด์ไม่ได้ แต่คุณควรจำกัดตรรกะการสร้างและการประกาศงานไว้ที่ปลั๊กอิน (ที่คุณหรือผู้อื่นกำหนด) และประกาศวิธีที่คุณต้องการใช้ตรรกะนั้นในไฟล์บิลด์

จะเกิดอะไรขึ้นเมื่อบิลด์ Gradle ทำงาน

การบิลด์ Gradle จะทำงานใน 3 ระยะ แต่ละเฟสจะเรียกใช้ส่วนต่างๆ ของโค้ดที่คุณกำหนดไว้ในไฟล์บิลด์

  • การเริ่มต้นจะกำหนดโปรเจ็กต์และโปรเจ็กต์ย่อยที่จะรวมไว้ในบิลด์ และตั้งค่าคลาสพาธที่มีไฟล์บิลด์และปลั๊กอินที่ใช้ ขั้นตอนนี้จะมุ่งเน้นที่ไฟล์การตั้งค่าที่คุณประกาศโปรเจ็กต์ที่จะสร้างและตำแหน่งที่จะดึงข้อมูลปลั๊กอินและไลบรารี
  • การกำหนดค่าจะลงทะเบียนงานสำหรับแต่ละโปรเจ็กต์ และเรียกใช้ไฟล์บิลด์เพื่อใช้ข้อกำหนดเฉพาะของบิลด์ของผู้ใช้ โปรดทราบว่าโค้ดการกำหนดค่าจะไม่มีสิทธิ์เข้าถึงข้อมูลหรือไฟล์ที่สร้างขึ้นระหว่างการดำเนินการ
  • การดำเนินการจะทำการ "สร้าง" แอปพลิเคชันจริง ผลลัพธ์ของการกำหนดค่าคือ Directed Acyclic Graph (DAG) ของงาน ซึ่งแสดงขั้นตอนการสร้างที่จำเป็นทั้งหมดที่ผู้ใช้ขอ (งานที่ระบุในบรรทัดคำสั่งหรือเป็นค่าเริ่มต้นในไฟล์บิลด์) กราฟนี้แสดงความสัมพันธ์ระหว่างงาน ไม่ว่าจะระบุไว้อย่างชัดเจนในการประกาศงาน หรืออิงตามอินพุตและเอาต์พุตของงาน หากงานมีอินพุตที่เป็นเอาต์พุตของงานอื่น งานนั้นจะต้องทำงานหลังจากงานอื่น ระยะนี้จะเรียกใช้งานที่ล้าสมัยตามลำดับที่กำหนดไว้ในกราฟ หากอินพุตของงานไม่มีการเปลี่ยนแปลงนับตั้งแต่การดำเนินการครั้งล่าสุด Gradle จะข้ามงานนั้นไป

ดูข้อมูลเพิ่มเติมได้ที่วงจรการสร้างของ Gradle

DSL การกำหนดค่า

Gradle ใช้ Domain-Specific Language (DSL) เพื่อกำหนดค่าบิลด์ แนวทางแบบประกาศนี้มุ่งเน้นที่การระบุข้อมูลของคุณมากกว่าการเขียนวิธีการทีละขั้นตอน (แบบบังคับ) คุณสามารถเขียนไฟล์บิลด์โดยใช้ Kotlin หรือ Groovy ก็ได้ แต่เราขอแนะนำอย่างยิ่งให้ใช้ Kotlin

DSL พยายามทำให้ทุกคน ผู้เชี่ยวชาญด้านโดเมน และโปรแกรมเมอร์มีส่วนร่วมในโปรเจ็กต์ได้ง่ายขึ้น โดยกำหนดภาษาขนาดเล็กที่แสดงข้อมูลในลักษณะที่เป็นธรรมชาติมากขึ้น ปลั๊กอิน Gradle สามารถขยาย DSL เพื่อกำหนดค่าข้อมูลที่จำเป็นสำหรับงานของตนได้

ตัวอย่างเช่น การกำหนดค่าส่วน Android ของบิลด์อาจมีลักษณะดังนี้

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)
        }
        // ...
    }
}

ดึงดูด

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

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

เบื้องหลังโค้ด DSL จะมีลักษณะคล้ายกับโค้ดต่อไปนี้

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

interface ApplicationExtension {
    var namespace: String?

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

    val defaultConfig: DefaultConfig

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

แต่ละบล็อกใน DSL จะแสดงด้วยฟังก์ชันที่ใช้ Lambda เพื่อกำหนดค่า และพร็อพเพอร์ตี้ที่มีชื่อเดียวกันเพื่อเข้าถึง ซึ่งจะทำให้โค้ดในไฟล์บิลด์ดูเหมือนข้อกำหนดข้อมูลมากขึ้น

ทรัพยากร Dependency ภายนอก

ระบบบิลด์ Maven ได้เปิดตัวข้อกำหนด Dependency, พื้นที่เก็บข้อมูล และระบบการจัดการ ไลบรารีจะจัดเก็บไว้ในที่เก็บ (เซิร์ฟเวอร์หรือไดเรกทอรี) พร้อมข้อมูลเมตา ซึ่งรวมถึงเวอร์ชันและการขึ้นต่อกันของไลบรารีอื่นๆ คุณระบุที่เก็บที่จะค้นหา เวอร์ชันของทรัพยากร Dependency ที่ต้องการใช้ และระบบบิลด์จะดาวน์โหลดทรัพยากรเหล่านั้นในระหว่างการบิลด์

อาร์ติแฟกต์ Maven จะระบุตามชื่อกลุ่ม (บริษัท นักพัฒนาแอป ฯลฯ) ชื่ออาร์ติแฟกต์ (ชื่อไลบรารี) และเวอร์ชันของอาร์ติแฟกต์นั้น ซึ่งโดยปกติจะแสดงเป็น group:artifact:version

แนวทางนี้ช่วยปรับปรุงการจัดการบิลด์ได้อย่างมาก คุณมักจะได้ยินว่าที่เก็บดังกล่าวเรียกว่า "ที่เก็บ Maven" แต่ทั้งหมดนี้เป็นเรื่องเกี่ยวกับวิธีแพ็กเกจและเผยแพร่อาร์ติแฟกต์ ที่เก็บและข้อมูลเมตาเหล่านี้มีการนำมาใช้ซ้ำในระบบบิลด์หลายระบบ ซึ่งรวมถึง Gradle (และ Gradle สามารถเผยแพร่ไปยังที่เก็บเหล่านี้ได้) ที่เก็บสาธารณะช่วยให้แชร์เพื่อให้ทุกคนใช้ได้ และที่เก็บของบริษัทจะเก็บทรัพยากร Dependency ภายในไว้ในองค์กร

นอกจากนี้ คุณยังแยกโมดูลโปรเจ็กต์ออกเป็นโปรเจ็กต์ย่อย (หรือที่เรียกว่า "โมดูล" ใน Android Studio) ซึ่งใช้เป็นทรัพยากร Dependency ได้ด้วย โปรเจ็กต์ย่อยแต่ละโปรเจ็กต์จะสร้างเอาต์พุต (เช่น JAR) ที่โปรเจ็กต์ย่อยหรือโปรเจ็กต์ระดับบนสุดใช้ได้ ซึ่งจะช่วยปรับปรุงเวลาในการสร้างโดยแยกส่วนที่ต้องสร้างใหม่ รวมถึงแยกความรับผิดชอบในแอปพลิเคชันได้ดียิ่งขึ้น

เราจะอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับวิธีระบุทรัพยากร Dependency ในหัวข้อเพิ่มทรัพยากร Dependency ของบิลด์

ตัวแปรบิลด์

เมื่อสร้างแอปพลิเคชัน Android โดยทั่วไปคุณจะต้องสร้างตัวแปรหลายรายการ ตัวแปรมีโค้ดที่แตกต่างกันหรือสร้างขึ้นด้วยตัวเลือกที่แตกต่างกัน และประกอบด้วยประเภทบิลด์และเวอร์ชันผลิตภัณฑ์

ประเภทบิลด์จะแตกต่างกันไปตามตัวเลือกบิลด์ที่ประกาศไว้ โดยค่าเริ่มต้น AGP จะตั้งค่าประเภทบิลด์เป็น "Release" และ "Debug" แต่คุณสามารถปรับเปลี่ยนและเพิ่มประเภทอื่นๆ ได้ (เช่น สำหรับการทดสอบภายในหรือการทดสอบเวอร์ชันทดลอง)

บิลด์การแก้ไขข้อบกพร่องจะไม่ลดขนาดหรือสร้างความสับสนให้กับแอปพลิเคชัน ซึ่งจะช่วยเร่งความเร็วในการสร้างและคงสัญลักษณ์ทั้งหมดไว้ตามเดิม นอกจากนี้ยังทำเครื่องหมายแอปพลิเคชันเป็น "แก้ไขข้อบกพร่องได้" โดยลงนามด้วยคีย์แก้ไขข้อบกพร่องทั่วไปและเปิดใช้สิทธิ์เข้าถึงไฟล์แอปพลิเคชันที่ติดตั้งในอุปกรณ์ ซึ่งช่วยให้สำรวจข้อมูลที่บันทึกไว้ในไฟล์และฐานข้อมูลขณะเรียกใช้แอปพลิเคชันได้

บิลด์ที่เผยแพร่จะเพิ่มประสิทธิภาพแอปพลิเคชัน รับรองด้วยคีย์การเผยแพร่ และปกป้องไฟล์แอปพลิเคชันที่ติดตั้ง

เมื่อใช้เวอร์ชันผลิตภัณฑ์ คุณจะเปลี่ยนแหล่งที่มาและตัวแปรทรัพยากร Dependency ที่รวมไว้สำหรับแอปพลิเคชันได้ เช่น คุณอาจต้องการสร้างเวอร์ชัน "เดโม" และ "เต็ม" สำหรับแอปพลิเคชัน หรืออาจเป็นเวอร์ชัน "ฟรี" และ "เสียค่าใช้จ่าย" คุณเขียนซอร์สทั่วไปในไดเรกทอรีชุดซอร์ส "หลัก" และลบล้างหรือเพิ่มซอร์สในชุดซอร์สที่ตั้งชื่อตามเวอร์ชัน

AGP จะสร้างตัวแปรสำหรับชุดค่าผสมของประเภทบิลด์และเวอร์ชันผลิตภัณฑ์แต่ละรายการ หากคุณไม่ได้กำหนด Flavor ระบบจะตั้งชื่อตัวแปรตามประเภทบิลด์ หากคุณกำหนดทั้ง 2 อย่าง ตัวแปรจะชื่อว่า <flavor><Buildtype> ตัวอย่างเช่น หากมีประเภทบิลด์ release และ debug รวมถึงเวอร์ชัน demo และ full AGP จะสร้างตัวแปรต่อไปนี้

  • demoRelease
  • demoDebug
  • fullRelease
  • fullDebug

ขั้นตอนถัดไป

เมื่อคุณได้เห็นแนวคิดการสร้างแล้ว ให้ดูโครงสร้างการสร้าง Android ในโปรเจ็กต์