การใช้ Build Flavours - จัดโครงสร้างโฟลเดอร์ซอร์สและ build.gradle อย่างถูกต้อง


166

โปรดทราบ:คำตอบแก้ไขหลังจากคำตอบของซาเวียร์

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

ขั้นตอน:

  1. สร้างโครงการ Android Studio ใหม่ชื่อ 'ทดสอบ'
  2. เปิด build.gradle * และเพิ่มบรรทัดต่อไปนี้:

    productFlavors {
    flavor1 {
        packageName 'com.android.studio.test.flavor1'
        }
    flavor2 {
        packageName 'com.android.studio.test.flavor2'
        }
    }
    
  3. หลังจากรีสตาร์ท Android Studio ตอนนี้ฉันเห็นรูปแบบการสร้าง 4 แบบในส่วน Build Variants หมายความว่าเราประสบความสำเร็จในการติดตั้งรสชาติผลิตภัณฑ์จนถึงขณะนี้ **
  4. สร้างโฟลเดอร์ซอร์สใหม่สำหรับflavor1 ; อย่างไรก็ตามฉันไม่แน่ใจว่าฉันทำถูกหรือไม่ นี่คือวิธีที่ฉันทำ:

    • โปรดทราบว่าชื่อแพ็คเกจของฉันสำหรับโครงการนี้คือ: com.foo.test
    • คลิกขวาที่srcโฟลเดอร์สำหรับ flavor1 src/flavor1/java/com/foo/test/MainActivity.javaจริงผมสร้างโฟลเดอร์ในแต่ละสำรวจในทางที่โครงสร้างเป็น
    • ข้างต้นทำงานได้ดีเนื่องจากโฟลเดอร์ 'java' เป็นสีน้ำเงินหมายความว่า IDE รู้ว่ามันเป็นไดเรกทอรีที่ใช้งานอยู่ นอกจากนี้แพคเกจถูกสร้างขึ้นโดยอัตโนมัติ อย่างไรก็ตามเรื่องนี้ฉันได้รับคำเตือนเกี่ยวกับการเรียนซ้ำ ดูภาพหน้าจอที่นี่
    • สำหรับ flavor2 ฉันพยายามสร้างแพ็คเกจด้วยตนเอง แต่โฟลเดอร์ 'src' สำหรับ flavor2 ดูเหมือนจะไม่เป็นสีน้ำเงินดังนั้นตัวเลือกต่าง ๆ เมื่อคลิกขวาและ 'แพ็คเกจใหม่' ไม่สามารถใช้ได้ ดูภาพได้ที่นี่
    • โปรดทราบว่าสำหรับ flavor1 ฉันยังสร้างไดเรกทอรี 'res' ซึ่งเปลี่ยนเป็นสีน้ำเงิน แต่แม้จะไม่มีความสามารถในการสร้างไฟล์ Android Resource หรือไดเรกทอรีทรัพยากร Andorid ในกรณีที่ฉันต้องการใช้ที่แตกต่างกัน resoruces สำหรับรสชาติที่แตกต่าง

ฉันกำลังทำอะไรผิดหรือเปล่า? หรือฉันกำลังพลาดอะไรอยู่? แจ้งให้เราทราบหากคุณต้องการข้อมูลเพิ่มเติม

* โครงการของฉันดูเหมือนจะมีไฟล์ build.gradle สองไฟล์ หนึ่งที่อยู่บนรูทของโฟลเดอร์โครงการ (\ GradleTest) อันนี้ว่างเปล่า อันที่สองที่อยู่บนรูตของโฟลเดอร์ย่อยของ \ GradleTest ที่มีป้ายกำกับ 'GradleTest' (GradleTest-GradleTest) นี่คืออันที่มีรหัสเมื่อเปิดอยู่แล้ว ดังนั้นนั่นคือสิ่งที่ฉันแก้ไข

** ฉันจะตรวจสอบการตั้งค่า gradle และเห็นได้ชัดว่าการใช้รถยนต์นำเข้าถูกเปิดใช้งานแล้ว อย่างไรก็ตามสิ่งนี้การเปลี่ยนแปลงไฟล์ build.gradle ไม่ได้อัพเดตชุดตัวเลือกการสร้างโดยอัตโนมัติ หมายเหตุ:ฉันได้ลองใช้ Build - สร้างโครงการใหม่และ / หรือ Build - สร้างโครงการโดยไม่ต้องไป ฉันยังคงต้องปิดโครงการและเปิดใหม่เพื่อให้การเปลี่ยนแปลงมีผล


โปรดทราบว่าอยู่ในขณะนี้ได้รับการสนับสนุนแทนapplicationId packageName
Hamzeh Soboh

คำตอบ:


220

หากคุณได้รับการตั้งค่าในสตูดิโอภายใต้ส่วน Gradle คุณสามารถเปิดใช้งานการนำเข้าอัตโนมัติสำหรับโครงการของคุณ (เราจะเปิดใช้งานสิ่งนี้ตามค่าเริ่มต้นในภายหลัง) สิ่งนี้จะทำให้ Studio นำเข้า build.gradle ของคุณใหม่ทุกครั้งที่คุณแก้ไข

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

ถ้าคุณดูที่การพูดคุยแบบ IO ของฉันคุณจะเห็นว่าเราผสมผสานคุณค่าจากรสชาติและประเภทการสร้างเข้าด้วยกันเพื่อสร้างตัวแปรได้อย่างไร

สำหรับซอร์ส Java:

src/main/java
src/flavor1/java
src/debug/java

เป็น 3 ทั้งหมดที่ใช้ในการสร้างเอาต์พุตเดียว ซึ่งหมายความว่าพวกเขาไม่สามารถกำหนดคลาสเดียวกันได้

หากคุณต้องการมีคลาสที่แตกต่างจากคลาสเดียวกันในสองรสชาติคุณจะต้องสร้างในทั้งสองรสชาติ

src/flavor1/java/com/foo/A.java
src/flavor2/java/com/foo/A.java

จากนั้นโค้ดของคุณใน src / main / java สามารถทำได้

import com.foo.A

จะใช้เวอร์ชั่นที่เหมาะสมของ com.foo.A ทั้งนี้ขึ้นอยู่กับรสชาติที่เลือก

นอกจากนี้ยังหมายถึงทั้งสองเวอร์ชันของ A ต้องมี API เดียวกัน (อย่างน้อยเมื่อมันมาถึง API ที่ใช้โดยคลาสใน src / main / java / ...

แก้ไขเพื่อให้ตรงกับคำถามที่แก้ไข

นอกจากนี้สิ่งสำคัญคือต้องใส่คลาส A เดียวกันในโฟลเดอร์ซอร์สที่ไม่ได้เกิดร่วมกัน ในกรณีนี้ src / flavor1 / java และ src / flavor2 / java จะไม่ถูกเลือกด้วยกัน แต่ main และ flavor1 คือ

หากคุณต้องการจัดทำกิจกรรมเวอร์ชันที่แตกต่างในรสชาติที่แตกต่างกันอย่าใส่ไว้ใน src / main / java

โปรดทราบว่าหากคุณมี 3 รสชาติและต้องการเพียงหนึ่งที่กำหนดเองสำหรับ flavor1 ในขณะที่ flavor2 และ flavor3 แบ่งปันกิจกรรมเดียวกันคุณสามารถสร้างโฟลเดอร์ซอร์สทั่วไปสำหรับกิจกรรมอื่น ๆ สองกิจกรรมนั้น คุณมีความยืดหยุ่นโดยรวมในการสร้างโฟลเดอร์ซอร์สใหม่และกำหนดค่าชุดซอร์สให้ใช้

ไปยังจุดอื่น ๆ ของคุณ:

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

ฉันคิดว่าเป็นเรื่องปกติที่คุณไม่สามารถสร้างไฟล์ทรัพยากรในโฟลเดอร์ res ระบบเมนูไม่ได้รับการอัพเดตเพื่อจัดการกับโฟลเดอร์ทรัพยากรพิเศษเหล่านี้ทั้งหมด นี้จะมาในภายหลัง


1
ฉันได้เพิ่มองค์ประกอบใหม่ในตอนท้ายของคำตอบของฉัน แต่สิ่งที่ซ้ำกันทำให้รู้สึก คุณไม่สามารถมีคลาสเดียวกันทั้งใน src / main / java และ src / flavor1 / java เนื่องจากทั้งสองถูกใช้เมื่อเลือก flavor1 ในคำตอบของฉันสังเกตว่าฉันใส่คลาสเดียวกันลงใน flavor1 / java เท่านั้นและ flavor2 / java อย่างเดียวเพราะมันพิเศษและไม่เคยเปิดใช้งานร่วมกัน
Xavier Ducrohet

เฮ้ซาเวียร์คุณช่วยให้ฉันอธิบายรายละเอียดเพิ่มเติมเกี่ยวกับวิธีที่ฉันสามารถใช้รุ่นที่แตกต่างกันของกิจกรรมในรสชาติของฉันได้อย่างไร ฉันมีโครงการทดสอบที่ฉันต้องการใช้ MainActivity รุ่นต่าง ๆ แต่ทั้ง apks (flavor1 และ flavor2) มีเฉพาะ main / java เท่านั้น เมื่อฉันไม่ใส่ MainActivity ไว้ใน main / java แอพจะขัดข้องเมื่อฉันเริ่มต้น
JensJensen

@ XavierDucrohet ทำอย่างไรถึงจะมีทรัพยากรที่แตกต่างกันรวมถึงโค้ดที่แตกต่างกันตามรสชาติ แต่มีในโมดูลที่แตกต่างกันเพื่อให้เราสามารถรวมหนึ่งโมดูลหรืออื่น ๆ ตามรสชาติโดยไม่ต้องผสมรหัสและทรัพยากรในโครงการรูทเดียวกัน รองรับหรือไม่
Valerio Santinelli

3
@ValerioSantinelli คุณสามารถทำสิ่งที่ต้องพึ่งพาต่อรสชาติ ใช้flavorCompile ...
Xavier Ducrohet

@XavierDucrohet ฉันลองสิ่งที่คุณแนะนำ แต่มันไม่ทำงานอย่างที่ฉันคาดไว้ คุณสามารถดูว่าโครงการของฉันมีโครงสร้างอย่างไร: stackoverflow.com/q/24410995/443136
Valerio Santinelli

19

"รสชาติผลิตภัณฑ์" บน Android

บางครั้งฉันถูกถามเกี่ยวกับวิธีการทำงานกับโฮสต์ไอคอนหรือชื่อแพคเกจที่แตกต่างกัน

มีเหตุผลมากมายในการทำเช่นนี้และวิธีง่ายๆในการดำเนินการ: Product Flavours

คุณสามารถกำหนดสคริปต์ build.gradle ของสิ่งเหล่านี้ฉันได้อธิบายมาก่อน

รสชาติ ของผลิตภัณฑ์ส่วนหนึ่งของบทความนี้เขียนขึ้นโดยคำนึงถึงรสชาติของผลิตภัณฑ์ดังนั้นอะไรบ้าง เกี่ยวกับเอกสาร Android:

รสชาติของผลิตภัณฑ์กำหนดรุ่นที่กำหนดเองของการสร้างแอปพลิเคชันโดยโครงการ โครงการเดียวสามารถมีรสชาติที่แตกต่างซึ่งเปลี่ยนแอปพลิเคชันที่สร้างขึ้น

คุณจะกำหนดมันได้อย่างไร คุณต้องเขียนบน build.gradle ซึ่งคุณต้องการกำหนดรสชาติ:

productFlavors {  
        ...
        devel {
            ...
        }

        prod {
            ...
        }
    }

ตอนนี้เราจะมีสองรสชาติที่แตกต่างกันในแอพของเรา คุณสามารถตรวจสอบได้บน Android Studio ภายในแท็บ Build Variants

สร้างสายพันธุ์

ชื่อแพคเกจหลาย ๆ

จะทำอย่างไรถ้าคุณต้องการติดตั้งในโทรศัพท์หนึ่งแอพที่มีสถานะการพัฒนาและอีกแอปหนึ่งสำหรับสถานะการผลิต อย่างที่คุณอาจทราบคุณสามารถติดตั้งเพียงหนึ่งแอปที่มีชื่อแพคเกจเดียวกัน (หากคุณพยายามติดตั้ง APK ใหม่บางอันที่มีการติดตั้งบนโทรศัพท์ของคุณมันจะพยายามอัปเดต)

สิ่งเดียวที่คุณต้องทำคือการกำหนดมันในแต่ละรสชาติผลิตภัณฑ์ของคุณ:

android {  
    productFlavors {
        devel {
            applicationId "zuul.com.android.devel"
        }
        prod {
            applicationId "zuul.com.android"
        }
    }
}

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

android {  
    productFlavors {
        devel {
            applicationId "zuul.com.android.devel"
            buildConfigField 'String', 'HOST', '"http://192.168.1.34:3000"'

        }

        prod {
            applicationId "zuul.com.android"
               buildConfigField 'String', 'HOST', '"http://api.zuul.com"'

        }
    }
}

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

public class RetrofitModule {

    public ZuulService getRestAdapter() {
        RestAdapter restAdapter = new RestAdapter.Builder()
                .setEndpoint(BuildConfig.HOST)
                .setLogLevel(RestAdapter.LogLevel.FULL)
                .build();
        return restAdapter.create(ZuulService.class);
    }

}

ดังที่คุณเห็นคุณเพียงแค่ใช้ BuildConfigclass เพื่อเข้าถึงตัวแปรที่คุณเพิ่งกำหนด

ตัวแปรใด ๆ ที่มีอยู่ในรหัสของคุณตัวแปร HOST ไม่ใช่ตัวแปรเดียวที่คุณสามารถเปิดเผยในรหัสของคุณ คุณสามารถทำสิ่งที่คุณต้องการ:

prod {  
    applicationId "zuul.com.android"
    buildConfigField 'String', 'HOST', '"http://api.zuul.com"'
    buildConfigField 'String', 'FLAVOR', '"prod"'
    buildConfigField "boolean", "REPORT_CRASHES", "true"
}

คุณสามารถเข้าถึงได้ดังนี้:

BuildConfig.HOST  
BuildConfig.FLAVOR  
BuildConfig.REPORT_CRASHES  

ไอคอนที่แตกต่างกันต่อรสชาติหากคุณต้องการไอคอนที่แตกต่างกันต่อรสชาติดังนั้นคุณสามารถตรวจจับได้ว่าคุณกำลังเปิด (คุณสามารถทำได้โดยใช้ชื่อด้วย ... แต่มันไม่พอดีกับพื้นที่!) คุณแค่มี เพื่อกำหนดโครงสร้างไดเรกทอรีใหม่สำหรับแต่ละรสชาติ

ในตัวอย่างที่ฉันเพิ่งใช้มีสองรสชาติ: devel และแยง จากนั้นเราสามารถกำหนดโครงสร้างไดเรกทอรีใหม่สองรายการเพื่อให้เราสามารถกำหนดทรัพยากรที่เราต้องการ:

โครงสร้าง

ใช้ได้กับทรัพยากรประเภทอื่น ๆ เช่นstrings.xml, integers.xml, arrays.xmlฯลฯ

กำหนดการตั้งค่าการลงชื่อ

ในการกำหนดค่าการกำหนดค่าการเซ็นชื่อด้วยตนเองสำหรับประเภทการสร้างรีลีสของคุณโดยใช้การกำหนดค่าการสร้างของ Gradle

1. สร้างที่เก็บคีย์ ที่เก็บคีย์คือไฟล์ไบนารีที่มีชุดของไพรเวตคีย์ คุณต้องเก็บที่เก็บคีย์ของคุณในที่ปลอดภัย 2. สร้างรหัสส่วนตัว รหัสส่วนตัวแสดงถึงเอนทิตีที่จะระบุด้วยแอปเช่นบุคคลหรือ บริษัท 3. เพิ่มการกำหนดค่าการลงชื่อเข้ากับไฟล์ build.gradle ระดับโมดูล:

android {
...
defaultConfig {...}
signingConfigs {
    release {
        storeFile file("myreleasekey.keystore")
        storePassword "password"
        keyAlias "MyReleaseKey"
        keyPassword "password"
    }
}
buildTypes {
    release {
        ...
        signingConfig signingConfigs.release
    }
}

}

สร้าง APK ที่ลงชื่อแล้ว:

หากต้องการสร้าง APK ที่ลงชื่อแล้วให้เลือกสร้าง> สร้าง APK ที่ลงชื่อแล้วจากเมนูหลัก แพ็คเกจในแอพ / บิลด์ / apk / แอพ - release.apk ตอนนี้ลงชื่อด้วยรหัสปลดล็อคของคุณแล้ว

อ้างอิง: https://developer.android.com/studio/build/build-variants.html#signing,http://blog.brainattica.com/how-to-work-with-flavours-on-android/


2
นี่คือเดียวกันblog.brainattica.com/how-to-work-with-flavours-on-android
Sreehari

7

ดูเหมือนว่าคุณจะต้องโหลดโครงการของคุณbuild.gradleหลังจากที่เพิ่มรสชาติใหม่ใน หลังจากนั้นคุณจะเห็น 4 บิลด์ Variants ในมุมมอง Build Variants (คุณเข้าถึงจากขอบด้านซ้ายของหน้าต่าง)

เกี่ยวกับไดเรกทอรีแหล่งที่มาดูเหมือนว่าคุณจะต้องสร้างพวกเขาด้วยมือ: และsrc/flavor1/java src/flavor2/javaคุณจะเห็นว่าการเปลี่ยนรสชาติในมุมมอง "Build Variants" จะเปลี่ยนไดเรกทอรีแหล่งที่ใช้งานอยู่ในปัจจุบัน (ไดเรกทอรีเป็นสีฟ้าเมื่อเป็นไดเรกทอรีแหล่งที่ใช้งานอยู่ )

สุดท้าย "gradle จะสร้าง sourceSets ใหม่สำหรับรสชาติใหม่ของคุณ" หมายถึงว่า gradle จะสร้างวัตถุandroid.sourceSets.flavor1และandroid.sourceSets.flavor2และคุณสามารถใช้พวกเขาในสคริปต์ build.gradle ของคุณ แต่วัตถุเหล่านั้นถูกสร้างขึ้นแบบไดนามิกนั่นคือเหตุผลที่คุณไม่เห็นพวกเขาในbuild.gradle(ฉันขอแนะนำให้คุณอ่าน: http://www.gradle.org/docs/current/userguide/tutorial_using_tasks.htmlโดยเฉพาะอย่างยิ่ง 6.6: มันอธิบาย การสร้างงานแบบไดนามิกสคริปต์ gradle เป็นสคริปต์ groovy ดังนั้นฉันขอแนะนำให้คุณทำความรู้จักกับ groovy ด้วย)


2
ฉันคิดว่าบันทึกการนำเข้าคือBuild Variantsมุมมองฉันไม่ได้สังเกตสิ่งนั้น
Chris.Jenkins

2

ฉันมีปัญหาเดียวกันเมื่อฉันย้ายโครงการของฉันไปที่ Gradle ปัญหาคือการสร้างไม่พบโฟลเดอร์ทรัพยากรที่เหมาะสม ฉันแก้ไขได้โดยการเพิ่มสิ่งนี้ภายใต้องค์ประกอบ Android ใน build.gradle:

sourceSets {
        main {
            res.srcDirs = ['myProject/res']
        }
    }

0

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

src/flavor1/java/com/foo/A.java

จะจับคู่

productFlavors {
  flavor1 {
    packageName 'com.android.studio.test.foobar'
  }
}

แต่

src/foobar/java/com/foo/A.java จะไม่ถูกใช้สำหรับการสร้าง flavor1


0

ในระดับ:

สำหรับประเภทการสร้างคุณต้องการเพียง:

buildTypes {
   release{
    //proguard, signing etc.
   }
   debug {
    //development
   }
  }
}

และสำหรับรสชาติที่คุณเพิ่มสิ่งที่คุณต้องการ

productFlavors {
    pro {
        applicationIdSuffix '.paid'
        buildConfigField 'boolean', 'PRO', 'true'
    }
    free {
        applicationIdSuffix '.free'
        buildConfigField 'boolean', 'PRO', 'false'
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.