【问题标题】:How to have two build flavors inherit from a root flavor in Android Studio?如何从 Android Studio 中的根风格继承两种构建风格?
【发布时间】:2014-12-25 12:10:46
【问题描述】:

我曾经有以下项目风格:

  1. 苹果
  2. 橙色

最初唯一的区别是applicationId/packageName。现在有一个不同的 java 文件。确切地说,是一个自定义的 ArrayAdapter。解决方案是创建src/Applesrc/Orange,它们都继承自src/main。我从src/main 中删除了java 文件,并将副本放入src/Applesrc/Orange 并进行了适当的修改。世界上一切都很好。

快进几周,现在 Apple 和 Orange 之间大约有 10 个 java 文件不同。再次......没什么大不了的。易于处理。将src/Applesrc/Orange 中的java 文件分开。

快进到今天。我需要稍微修改一下,因为我想拥有每个版本的免费和高级版本。免费版和高级版仅在 URL 上有所不同。我打算简单地创建新类型,称为:

  1. 苹果免费
  2. ApplePremium
  3. 无橙
  4. OrangePremium

但我有一个难题。从现在开始,src/Applesrc/Orange 有 10 个不同的文件已更改...如果我更改 AppleFree 中的任何 java 文件,我必须确保在 ApplePremium 中执行相同操作。我有点处于十字路口,希望我的问题在这一点上是有道理的。我提出了三种可能的解决方案,但我不确定如何实施它们/正确的方法是什么/解决方案不是我想要的。

解决方案 1:

使用 if 语句 if (BuildConfig.FLAVOR==appleFree) {//use free Url} else {// use premium url}

问题: 两个 Url 在技术上都编译到 apk 中。我不想要这个。

解决方案 2:

让 src/AppleFree 和 src/ApplePremium 以某种方式从 src/Apple 父目录继承。

问题:不知道该怎么做。

解决方案 3:

像这样在 build.gradle 中添加免费和高级网址吗?

    productFlavors {
            appleFree {
                applicationId "com.example.apple.free"
                versionName "1.0"
                url "http://freeurl.com"
                versionCode 1
            }
            applePremium {
                applicationId "com.example.apple.premium"
                versionName "1.0"
                url "http://premiumurl.com"
                versionCode 1
            }
            orangeFree {
                applicationId "com.example.orange.free"
                versionName "1.0"
                versionCode 1
                url "http://freeurl.com"


            }
            orangePremium {
                applicationId "com.example.orange.premium"
                url "http://premiumurl.com"
                versionName "1.0"
                versionCode 1

            }
        } 

问题:不确定如何实现。

任何提示都是有帮助的。

编辑:

最终解决方案?

flavorGroups 'fruit', 'paid'

productFlavors {
    apple {
        flavorGroup 'fruit'
    }
    orange {
        flavorGroup 'fruit'
    }
    free {
        flavorGroup 'paid'
    }
    premium {
        flavorGroup 'paid'
    }
    appleFree {
        applicationId "com.example.apple.free"
        versionName "1.0"
        buildConfigField 'String', 'BASE_URL', 'http://freeurl.com'
        versionCode 1
    }
    applePremium {
        applicationId "com.example.apple.premium"
        versionName "1.0"
        buildConfigField 'String', 'BASE_URL', 'http://premiumurl.com'
        versionCode 1
    }
    orangeFree {
        applicationId "com.example.orange.free"
        versionName "1.0"
        versionCode 1
        buildConfigField 'String', 'BASE_URL', 'http://freeurl.com'
    }
    orangePremium {
        applicationId "com.example.orange.premium"
        buildConfigField 'String', 'BASE_URL', 'http://premiumurl.com' 
        versionName "1.0"
        versionCode 1
    }
    }

【问题讨论】:

  • 对于解决方案 1,编译器应该优化掉未使用的代码,因此它实际上不会生成二进制文件。
  • 真的吗?我相信我在#android-dev irc 上四处询问,似乎没有人对此完全确定。如果没有进入二进制文件会很好。无论如何要确定? (除了反编译)对我来说,接受 ADT 开发人员的话就足够了。不过,这给我留下了解决方案 #1 的另一个问题。如何使productFlavors {}中的appleFreeapplePremiumsrc/apple相关联?
  • 这是 Java 编译器的一个特性,它优化了具有静态最终条件的 if 语句的未使用代码路径。我知道BuildConfig.DEBUG 肯定有效。我会仔细检查BuildConfig.FLAVOR,因为它是String,但我希望它会起作用。我没有时间写一个完整的答案,但你应该调查风味组:tools.android.com/tech-docs/new-build-system/…
  • 感谢风味组。有这样的东西存在是有道理的。我将在今天晚些时候尝试使用 BuildConfig.FLAVOR 反编译示例应用程序,如果您对编译器的看法是正确的,那么似乎解决方案 1 可能是可行的方法。感谢您为我指明正确的方向。
  • 根据我的测试,如果您对静态最终字符串执行条件,它看起来不会剥离代码。布尔值按预期工作。

标签: android gradle android-studio android-gradle-plugin build.gradle


【解决方案1】:

您的问题有很多可能的解决方案。最原生的 Gradle 解决方案是使用 Flavor Dimensions,如 http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Multi-flavor-variants 中所述

这也与您对解决方案 2 的想法类似。

它会像这样工作:

flavorDimensions 'fruit', 'paid'

productFlavors {
    apple {
        dimension 'fruit'
    }
    orange {
        dimension 'fruit'
    }
    free {
        dimension 'paid'
    }
    premium {
        dimension 'paid'
    }
}

这将为您提供构建变体(和源文件夹),在其中组合每个风味维度的所有可能性,保持与您在 flavorDimensions 语句中指定组的顺序相同(即 appleFree ,而不是freeApple),因此:

* appleFree
* applePremium
* orangeFree
* orangePremium

在您的 src/ 文件夹中,您可以有以下可能性:

* src/main
* src/apple
* src/orange
* src/free
* src/premium
* src/appleFree
* src/applePremium
* src/orangeFree
* src/orangePremium

解决方案 3

您可以使用buildConfigField 来逐个指定BuildConfig 类中的常量:

productFlavors {
    appleFree {
        buildConfigField 'String', 'MY_URL', 'value1'
    }
    applePremium {
        buildConfigField 'String', 'MY_URL', 'value2'
    }
    orangeFree {
        buildConfigField 'String', 'MY_URL', 'value3'
    }
    orangePremium {
        buildConfigField 'String', 'MY_URL', 'value4'
    }

解决方案 1

我试图按照解决方案 1 的方式进行一些工作,但它不适用于您的确切用例。如果您在 Java 中有一个 if 条件来测试声明为 static final 的布尔值,那么编译器可以静态确定代码是否可访问,如果不是,它将删除它。因此:

static final boolean DEBUG = false;

...

if (DEBUG) {
    // do something
}

// do something 中的代码根本不会被编译。这是 Java 编译器有意并记录在案的行为,它允许您编写昂贵的调试代码,这些代码不会编译到您的发布二进制文件中。正是出于这个原因,BuildConfig.DEBUG 被声明为 static final

有一个BuildConfig.FLAVOR,但它被定义为String,你不会得到同样的好处:

static final String FLAVOR = "orange";

...

if (FLAVOR.equals("apple")) {
    // do something
}

编译器不够聪明,无法进行静态分析,看到 // do something 无法访问,不编译它。 请注意,它在运行时可以正常工作,但死代码将包含在您的二进制文件中。

但是,如果它适合您,您可以从上面窃取 buildConfigField 方法,并在某些变体中定义一个额外的布尔变量,以允许有条件地编译代码。这比直接定义字符串更复杂解决方案 3,但如果您发现自己想要区分行为而不费心制作特定于风味的子类,您可以走这条路。

【讨论】:

  • 还有一件事...我想我会采取一种混合的方法来解决这个问题。我想使用解决方案 3,因为我可以在一个文件中看到构建之间的所有不同因素。我仍然有一个问题,如果我在 appleFree 中更改 1 个文件,我必须在 applePremium 中进行更改。我将如何解决?我无法在解决方案 2 中使用您的方法,因为这样我就无法在 build.gradle 中提供我唯一的 applicationId。
  • 您说的是所有苹果类型之间的通用代码?也许您最好的选择是从解决方案 1 中选择风味组,但从解决方案 3 中进行 BuildConfig 自定义。我认为您可以在使用风味组时为每个变体指定 buildConfigFields。我知道您不能在风味 + 构建类型组合中指定它们。测试一下,看看会发生什么。
  • 我可以为清单中的内容做buildConfigFields 吗? (即applicationIdversionNameversionCode)是的,AppleFree 和 ApplePremium 之间的代码是相同的,除了 url。橙子也是一样。除了 URL 之外,所有橙色变体都有相同的代码。
  • 太糟糕了——看起来它想把orangeFree当作一个新事物,而不是orange + Free的组合。这种混合方法可能行不通。
  • 函数已重命名为 flavorDimensions 和 flavorDimension
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-11
  • 2016-08-31
  • 1970-01-01
  • 2013-04-30
  • 1970-01-01
相关资源
最近更新 更多