แอพหลายรสชาติตามไลบรารีหลายรสใน Android Gradle


102

แอปของฉันมีหลายรสชาติสำหรับระบบการเรียกเก็บเงินในแอปหลายตลาด

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

คำถามคือห้องสมุด android สามารถมีรสชาติของผลิตภัณฑ์ได้หรือไม่?

ถ้าเป็นเช่นนั้นฉันจะรวมรสชาติที่แตกต่างกันในรสชาติของแอปได้อย่างไร

ฉันค้นหาข้อมูลมากมายและไม่พบอะไรเกี่ยวกับสถานการณ์นี้ สิ่งเดียวที่ฉันพบคือสิ่งนี้ในhttp://tools.android.com/tech-docs/new-build-system/user-guide :

dependencies {
    flavor1Compile project(path: ':lib1', configuration: 'flavor1Release')
    flavor2Compile project(path: ':lib1', configuration: 'flavor2Release')
}

ฉันเปลี่ยนการกำหนดค่าเป็นอย่างอื่น แต่ใช้งานไม่ได้!

ฉันใช้ android studio 0.8.2


หลังจากการค้นหาหลายครั้งฉันไม่พบวิธีที่จะบรรลุสิ่งนี้แม้ว่าฉันจะอัปเกรดปลั๊กอิน Android เป็น3.4.2เวอร์ชันล่าสุดและไล่ระดับเป็นเวอร์ชันล่าสุด5.5.1แต่ก็ยังล้มเหลวด้วยเวลารวบรวมหรือการเชื่อมโยงทรัพยากรล้มเหลวใน aapt หรือไม่พบสัญลักษณ์ที่อยู่ในไลบรารี โมดูล
VinceStyling

คำตอบ:


141

ในที่สุดฉันก็พบวิธีการทำสิ่งนี้ฉันจะอธิบายที่นี่สำหรับคนอื่น ๆ ที่ประสบปัญหาเดียวกัน:

ส่วนสำคัญคือการตั้งค่า PublishNonDefault เป็น true ในไลบรารี build.gradle จากนั้นคุณต้องกำหนดการอ้างอิงตามที่แนะนำโดยคู่มือผู้ใช้

โครงการทั้งหมดจะเป็นดังนี้:

ไลบรารี build.gradle:

apply plugin: 'com.android.library'

android {        
    ....
    publishNonDefault true
    productFlavors {
        market1 {}
        market2 {}
    }
}

โครงการ build.gradle:

apply plugin: 'com.android.application'

android {
    ....
    productFlavors {
        market1 {}
        market2 {}
    }
}

dependencies {
    ....
    market1Compile project(path: ':lib', configuration: 'market1Release')
    market2Compile project(path: ':lib', configuration: 'market2Release')
}

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

หากคุณมีโมดูลแอปหลายตัวตามไลบรารี Android Studio จะบ่นเกี่ยวกับความขัดแย้งในการเลือกตัวแปรไม่เป็นไรเพียงแค่เพิกเฉย

ป้อนคำอธิบายภาพที่นี่


ขอบคุณสำหรับการแบ่งปันตอนนี้ฉันสามารถกำจัดวิธีแก้ปัญหา defaultPublishConfig ของฉันได้แล้ว
Delblanco

2
การเรียกใช้ AS 1.1.0 วิธีแก้ปัญหาข้างต้นดูเหมือนจะยังคงใช้งานได้ แต่ 1) ตัวเลือกการสร้างการดีบัก / รีลีสหายไปและดูเหมือนว่าฉันจะมีปัญหากับ AIDL ที่พบในไลบรารีซึ่งไม่สามารถสร้างรหัสที่เหมาะสมได้บ่อยนัก มีความคิดเห็นเกี่ยวกับเรื่องนี้ไหม
3c71

1
@IgorGanapolsky buildTypes ไม่มีส่วนเกี่ยวข้องกับสิ่งนี้ ทุกรสชาติมีบิวด์ทุกประเภท (โดยปกติจะดีบั๊กและรีลีส) และทั้งหมดนี้ใช้ได้กับแนวทางนี้
อาลี

1
@ An-droid มันกำหนดห้องสมุดที่จะใช้สำหรับ market1 รสชาติ!
Ali

1
เหตุใดจึงตั้งค่าเป็นประเภทบิวด์ "ปล่อย" มีการเลือกประเภทบิวด์ "release" ในระหว่างการสร้างดีบักหรือไม่
WindRider

35

มีปัญหาหนึ่งกับคำตอบของอาลี เรากำลังสูญเสียมิติที่สำคัญอย่างหนึ่งในรูปแบบการสร้างของเรา หากเราต้องการมีตัวเลือกทั้งหมด (ในตัวอย่างของฉันด้านล่าง 4 (2 x 2)) เราก็ต้องเพิ่มการกำหนดค่าที่กำหนดเองในไฟล์build.gradle ของโมดูลหลักเพื่อให้สามารถใช้ multi-buildType หลายรสชาติในรูปแบบBuild Variants. นอกจากนี้เรายังต้องตั้งค่าpublishedNonDefault trueในไฟล์build.gradle ของโมดูลไลบรารี

ตัวอย่างวิธีแก้ปัญหา:

Lib build.gradle

android {

    publishNonDefault true

    buildTypes {
        release {
        }
        debug {
        }
    }
    productFlavors {
        free {
        }
        paid {
        }
    }
}

แอพ build.gradle

android {

    buildTypes {
        debug {
        }
        release {
        }
    }
    productFlavors {
        free {
        }
        paid {
        }
    }
}

configurations {
    freeDebugCompile
    paidDebugCompile
    freeReleaseCompile
    paidReleaseCompile
}

dependencies {

    freeDebugCompile project(path: ':lib', configuration: 'freeDebug')
    paidDebugCompile project(path: ':lib', configuration: 'paidDebug')
    freeReleaseCompile project(path: ':lib', configuration: 'freeRelease')
    paidReleaseCompile project(path: ':lib', configuration: 'paidRelease')

}

หลังจากทำสิ่งเดียวกันในแอปพลิเคชันของฉันเกิดError:java.lang.RuntimeException: Error: more than one library with package nameขึ้น
Chetan Joshi

22

อัปเดตสำหรับ Android Plugin 3.0.0 และสูงกว่า

ตามเอกสารอย่างเป็นทางการของ Android - ย้ายการกำหนดค่าการอ้างอิงสำหรับโมดูลภายในเครื่อง

ด้วยความละเอียดการอ้างอิงตัวแปรที่รับรู้คุณไม่จำเป็นต้องใช้การกำหนดค่าเฉพาะตัวแปรเช่น freeDebugImplementation สำหรับการพึ่งพาโมดูลภายในอีกต่อไปปลั๊กอินจะดูแลสิ่งนี้ให้คุณ

คุณควรกำหนดค่าการอ้างอิงของคุณแทนดังนี้:

dependencies {
    // This is the old method and no longer works for local
    // library modules:
    // debugImplementation project(path: ':library', configuration: 'debug')
    // releaseImplementation project(path: ':library', configuration: 'release')

    // Instead, simply use the following to take advantage of
    // variant-aware dependency resolution. You can learn more about
    // the 'implementation' configuration in the section about
    // new dependency configurations.
    implementation project(':library')

    // You can, however, keep using variant-specific configurations when
    // targeting external dependencies. The following line adds 'app-magic'
    // as a dependency to only the "debug" version of your module.

    debugImplementation 'com.example.android:app-magic:12.3'
}

ดังนั้นในคำตอบของอาลีเปลี่ยน

dependencies {
    ....
    market1Compile project(path: ':lib', configuration: 'market1Release')
    market2Compile project(path: ':lib', configuration: 'market2Release')
}

ถึง

implementation project(':lib')

และปลั๊กอินจะดูแลการกำหนดค่าตัวแปรโดยอัตโนมัติ หวังว่าจะช่วยให้ผู้อื่นอัปเกรด Android Studio Plugin เป็น 3.0.0 และสูงกว่า


7

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

ใน build.gradle ของ mylibrary

apply plugin: 'com.android.library'

android {        
    ....
    flavorDimensions "mylibFlavor"

    productFlavors {
        market1
        market2
    }
}

build.gradle ของแอปพลิเคชัน:

apply plugin: 'com.android.application'

android {
    ....
    flavorDimensions "mylibFlavor", "appFlavor"
    productFlavors {
        market1 {
            dimension "mylibFlavor"
        }
        market2 {
            dimension "mylibFlavor"
        }
        common1 {
            dimension "appFlavor"
        }
        common2 {
            dimension "appFlavor"
        }
    }
}

dependencies {
    ....
    implementation project(path: ':mylibrary')
}

หลังจากซิงค์คุณสามารถเปลี่ยนตัวเลือกทั้งหมดในหน้าต่าง Build Variants: ป้อนคำอธิบายภาพที่นี่


แต่ถ้าฉันไม่ต้องการให้มีรสชาติเหมือนกันในโมดูลแอปหลักล่ะ? สมมติว่าฉันมีโมดูลแอปหลายตัวที่มีรสชาติเฉพาะของตัวเองและโมดูลทั่วไปหนึ่งโมดูลที่มีรสชาติของตัวเองและฉันต้องการใช้ในแอปของฉันที่มีรสชาติเฉพาะ คุณจะทำอย่างไร? มันไม่สมเหตุสมผลที่จะคัดลอกรสชาติ lib ของฉันไปยังแอพทั้งหมด
Billda

@Billda คุณไม่จำเป็นต้องคัดลอกทั้งหมดเพียงแค่เก็บ productFlavor เดียวกันไว้ในแอปสำหรับตัวอย่างของฉันฉันสามารถเก็บ market1 หรือ market2 ไว้ใน build.gradle ของแอปพลิเคชันได้
JiajiaGu

2

เพื่อให้รสชาติทำงานบนไลบรารี AAR คุณต้องกำหนด defaultPublishConfig ในไฟล์ build.gradle ของโมดูลไลบรารี Android ของคุณ

สำหรับข้อมูลเพิ่มเติมโปรดดูที่: สิ่งพิมพ์ห้องสมุด

สิ่งพิมพ์ของห้องสมุด

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

แอนดรอยด์ {defaultPublishConfig "debug"}

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

แอนดรอยด์ {defaultPublishConfig "flavour1Debug"}


1

ในขณะนี้ยังไม่สามารถทำได้แม้ว่าฉันจะจำคุณสมบัติที่ต้องการเพิ่มได้อย่างถูกต้อง (แก้ไข 2: ลิงค์ , link2 )

แก้ไข: ในขณะนี้ฉันใช้defaultPublishConfigตัวเลือกเพื่อประกาศว่าจะเผยแพร่รูปแบบไลบรารีใดบ้าง:

android {
    defaultPublishConfig fullRelease
    defaultPublishConfig demoRelease 
}

1
ดังนั้นทุกครั้งที่ฉันจะรวบรวมแอปฉันต้องเปลี่ยนสิ่งนี้ใน build.gradle ของไลบรารี?
อาลี

ใช่แล้ว ... ทุกครั้งที่คุณต้องการรวบรวมแอปด้วยรสชาติที่แตกต่างกัน
Delblanco

จริงๆแล้วเมื่อฉันกำหนดรสชาติสำหรับโมดูลไลบรารีแพ็กเกจ R ที่สืบทอดมาจะไม่พบโมดูลแอป
อาลี

คุณซิงค์ไฟล์ gradle ใน AS หรือไม่
Delblanco

@Delblanco สิ่งนี้ดูเหมือนจะใช้แรงงานคนและเปราะมาก (นักพัฒนาขี้เกียจและลืมแก้ไขไฟล์ build.gradle)
IgorGanapolsky

1

ฉันรู้ว่าหัวเรื่องนี้ถูกปิดไปแล้ว แต่เป็นเพียงการอัปเดตด้วย gradle 3.0 โปรดดูสิ่งนี้: https://developer.android.com/studio/build/gradle-plugin-3-0-0-migration.html#variant_awareและ grep matchingFallbacksและmissingDimensionStrategy. ตอนนี้เป็นวิธีที่ง่ายกว่าในการประกาศการอ้างอิงระหว่างรสชาติของโมดูล

... และในกรณีที่แม่นยำนี้กับ gradle3.0 เนื่องจากรสชาติมีชื่อเดียวกัน gradle จะแมปพวกมันอย่างน่าอัศจรรย์ไม่จำเป็นต้องมีการกำหนดค่าใด ๆ


สำหรับฉันดูเหมือนว่าสิ่งที่สร้างรันไทม์จะถูกข้ามไป ตัวอย่างเช่น simonvt-> schematics generation ไม่ทำงานอีกต่อไปด้วยวิธีใหม่สำหรับฉัน : - /
Stefan Sprenger

1

ฉันยังพบปัญหาในการรวบรวมโมดูลสำหรับตัวเลือกต่างๆ

สิ่งที่ฉันพบ:

ดูเหมือนว่าเราไม่จำเป็นต้องเพิ่มpublishNonDefault trueเข้าไปใน lib ของbuild.gradleไฟล์ตั้งแต่Gradle 3.0.1

หลังจากแยกคลาสแล้วBaseExtensionพบสิ่งนี้:

public void setPublishNonDefault(boolean publishNonDefault) {
   this.logger.warn("publishNonDefault is deprecated and has no effect anymore. All variants are now published.");
}

และแทนที่จะ:

dependencies {
...
   Compile project(path: ':lib', configuration: 'config1Debug')
}

เราควรใช้:

dependencies {
...
   implementation project(':lib')
}

สิ่งเดียวที่สำคัญคือการเพิ่มconfigurations {...}ส่วนหนึ่งให้กับbuild.gradle.

ดังนั้นตัวแปรสุดท้ายของbuild.gradleไฟล์ของแอปคือ:

buildTypes {
   debug {
      ...
   }

   release {
      ...
   }
}

flavorDimensions "productType", "serverType"
productFlavors {
   Free {
      dimension "productType"
      ...
   }
   Paid {
      dimension "productType"
      ...
   }
   Test {
      dimension "serverType"
      ...
   }
   Prod {
      dimension "serverType"
      ...
   }
}

configurations {
   FreeTestDebug
   FreeTestRelease
   FreeProdDebug
   FreeProdRelease
   PaidTestDebug
   PaidTestRelease
   PaidProdDebug
   PaidProdRelease
}

dependencies {
   implementation fileTree(dir: 'libs', include: ['*.jar'])
   implementation project(':lib')
   ...
}

นอกจากนี้คุณสามารถใช้รูปแบบตัวกรองเพื่อ จำกัด รูปแบบบิวด์

Ps อย่าลืมรวมโมดูลในsettings.gradleไฟล์เช่น:

include ':app'
include ':lib'
project(':lib').projectDir = new File('app/libs/lib')

คุณช่วยอธิบายได้ไหมว่า scrip จะกำหนดสภาพอากาศเพื่อรวมไลบรารีไว้ในการกำหนดค่าบางอย่างได้อย่างไร? ฉันหมายความว่าฉันมีกรณีที่จำเป็นต้องใช้ lib สำหรับรสชาติบางอย่าง แต่ฉันไม่จำเป็นต้องใช้มันสำหรับรสชาติอื่น
Jenya Kirmiza

ไม่ได้อยู่ในสถานการณ์ดังกล่าว แต่ Google กวดวิชาdeveloper.android.com/studio/build/dependenciesแนะนำให้เพิ่มคำนำหน้าก่อนคำสั่ง "ใช้งาน" ในบล็อก "การอ้างอิง {... }" ได้แก่ การอ้างอิง {paidImplementation project (': lib')} หรือ dependencies {debugImplementation project (': lib')} หรือการพึ่งพาชุดค่าผสมตัวแปรใด ๆ {paidProdDebugImplementation project (': lib')} ลองดูและให้ข้อเสนอแนะกับเรา :)
Sergio
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.