问题描述
我曾经有以下项目风格:
I used to have the following project flavors:
- 苹果
- 橙色
最初唯一的区别是 applicationId/packageName
.现在有一个不同的 java 文件.确切地说,是一个自定义的 ArrayAdapter.解决方案是创建 src/Apple
和 src/Orange
并且都继承自 src/main
.我从 src/main
中删除了 java 文件,并将副本放入 src/Apple
和 src/Orange
并进行了适当的修改.世界上一切都很好.
Originally the only difference was the applicationId/packageName
. Now there is a single java file that is different. A custom ArrayAdapter to be exact. The solution was to create src/Apple
and src/Orange
and both inherit from src/main
. I removed the java file from src/main
and put a copy into src/Apple
and src/Orange
and modified it appropriately. All was good in the world.
快进几周,现在 Apple 和 Orange 之间大约有 10 个 java 文件不同.再次......没什么大不了的.易于处理.将 src/Apple
和 src/Orange
中的 java 文件分开.
Fast forward a few weeks, now there are about 10 java files that differ between Apple and Orange. Again... no big deal. Easy to handle. Separate java files in src/Apple
and src/Orange
.
快进到今天.我需要稍微修改一下,因为我想拥有每个版本的免费和高级版本.免费版和高级版仅在 URL 上有所不同.我打算简单地创建新类型,称为:
Fast forward to today. I need to modify things up a bit, because I want to have a free and premium version of each. The free and premium versions only differ by a URL. I was going to simply create the new types called:
- Apple免费
- ApplePremium
- OrangeFree
- OrangePremium
但我有一个难题.从现在开始 src/Apple
和 src/Orange
有 10 个不同的文件已更改...如果我更改 AppleFree
中的任何 java 文件,我必须确保我在 ApplePremium
中也这样做.我有点处于十字路口,希望我的问题在这一点上是有道理的.我提出了三种可能的解决方案,但我不确定我将如何实施它们/正确的方法是什么/解决方案不是我想要的.
I have a dilema though. Since now src/Apple
and src/Orange
have 10 different files that have been changed... if I change any java file in AppleFree
I have to make sure I do the same in ApplePremium
. I'm kind of at a crossroads and hope my question makes sense at this point. I have come up with three possible solutions, but I'm not sure how I would implement them/what would be the correct approach/the solution is not what I want.
使用 if 语句if (BuildConfig.FLAVOR==appleFree) {//使用免费网址} else {//使用高级网址}
问题: 两个 Url 在技术上都编译到 apk 中.我不想要这个.
Issue: Both Urls are technically compiled into the apk. I do not want this.
让 src/AppleFree 和 src/ApplePremium 以某种方式从 src/Apple 父目录继承.
Have src/AppleFree and src/ApplePremium inherit from an src/Apple parent directory somehow.
问题:不知道该怎么做.
像这样在 build.gradle 中添加免费和高级网址吗?
Add the free and premium url right in build.gradle like so?
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
}
}
问题:不确定如何实现.
任何提示都是有帮助的.
Any tips are helpful.
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
}
}
推荐答案
您的问题有很多可能的解决方案.最原生的 Gradle 解决方案是使用 Flavor Dimensions,如 http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Multi-flavor-variants
There are many possible solutions to your problem. The most native-Gradle solution would be to use Flavor Dimensions, as documented in http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Multi-flavor-variants
这也与您对解决方案 2 的想法类似.
This is also similar to what you were thinking about with Solution 2.
它会像这样工作:
flavorDimensions 'fruit', 'paid'
productFlavors {
apple {
dimension 'fruit'
}
orange {
dimension 'fruit'
}
free {
dimension 'paid'
}
premium {
dimension 'paid'
}
}
这将为您提供构建变体(和源文件夹),在其中组合每个风味维度的所有可能性,保持与您在 flavorDimensions
语句中指定组的顺序相同的顺序(即它是 appleFree
,而不是 freeApple
),因此:
This will give you build variants (and source folders) where it does the combination of all possibilities out of each flavor dimension, maintaining the same order as how the groups are specified in your flavorDimensions
statement (i.e. it's appleFree
, not freeApple
), thus:
* appleFree
* applePremium
* orangeFree
* orangePremium
在您的 src/ 文件夹中,您可以有以下可能性:
in your src/ folder, you can have these possibilities:
* src/main
* src/apple
* src/orange
* src/free
* src/premium
* src/appleFree
* src/applePremium
* src/orangeFree
* src/orangePremium
解决方案 3
您可以使用 buildConfigField
来指定进入 BuildConfig
类的常量:
You can use the buildConfigField
to specify constants that go in the BuildConfig
class on a flavor-by-flavor basis:
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
的布尔值,那么编译器可以静态确定代码是否可访问,并将其剥离如果不是.因此:
I was trying to work up something along the lines of Solution 1, but it won't work well for your exact use case. If you have an if
condition in Java that tests against a boolean that's declared static final
then the compiler can determine statically whether the code is reachable or not, and it will strip it if it's not. Thus:
static final boolean DEBUG = false;
...
if (DEBUG) {
// do something
}
//do something
中的代码根本不会被编译.这是 Java 编译器有意并记录在案的行为,允许您编写不会编译到发布二进制文件中的昂贵调试代码.正是出于这个原因,BuildConfig.DEBUG
被声明为 static final
.
The code in // do something
won't get compiled at all. This is an intentional and documented behavior on the part of the Java compiler, and allows you to write expensive debug code that won't get compiled into your release binary. BuildConfig.DEBUG
is declared as static final
for this exact reason.
有一个BuildConfig.FLAVOR
,但它被定义为String
,你不会得到同样的好处:
There's a BuildConfig.FLAVOR
, but it's defined as String
, and you don't get the same benefit:
static final String FLAVOR = "orange";
...
if (FLAVOR.equals("apple")) {
// do something
}
编译器不够聪明,无法进行静态分析,看到 //do something
无法访问,就不编译了.请注意,它在运行时可以正常工作,但死代码将包含在您的二进制文件中.
The compiler isn't smart enough to do static analysis, see that // do something
is unreachable, and not compile it. Note that it will work fine at runtime, but that dead code will be included in your binary.
不过,如果它适合您,您可以从上面窃取 buildConfigField
方法,并在某些变体中定义一个额外的布尔变量,以允许有条件地编译代码.这比定义更复杂像 Solution 3 中那样直接使用字符串,但是如果您发现自己想要区分行为而不费心制作特定于风味的子类,您可以走这条路.
If it suits you, though, you could steal the buildConfigField
approach from above and define an extra boolean variable in some variants that could allow code to be conditionally compiled in. This is more complex than defining the string directly as in Solution 3, but if you find yourself wanting to differentiate behavior without going through the trouble of making flavor-specific subclasses, you could go this route.
这篇关于如何让两种构建风格继承自 Android Studio 中的根风格?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!