使用 CSV/TXT 文件中的名称列表(带有属性)动态生成 productFlavors 和 sourceSets
Dynamically generating productFlavors and sourceSets using a list of names (with properties) in a CSV/TXT file
这个问题是 的延续,我想进一步改进。
我可以使用以下代码在 sourceSets
下对口味(具有通用配置)进行分组:
(从上面链接问题中的天才那里得到的)
import com.android.build.gradle.api.AndroidSourceSet
android {
sourceSets {
[flavor2, flavor4].each { AndroidSourceSet ss ->
ss.assets.srcDirs = ['repo-assets/flavor2']
ss.res.srcDirs = ['repo-res/flavor2']
}
}
}
现在,我想知道是否可以从以下任何一项中提取列表 [flavor2, flavor4]
:
- 一个 XML 文件,我可以对其进行迭代以获得所有口味(我将把它放在那里)
- 我可以在其上迭代和获取值的 CSV 文件。
- 自定义 class,我可以将其写入单独的文件并从 class 中的静态成员获取数据。
除了风味名称外,我打算将以下内容存储在外部源(以上之一)中:
- 应用程序 ID(我将拉到
productFlavors
)
- 广告单元 ID(每种口味两个)
- 其他一些自定义值,如类别等
PORPOSE: 我想编写一段通用代码来迭代和动态创建 productFlavors
和 sourceSets
。我已将 sourceSets
概括为将近 90%,现在一个街区足以满足所有口味。
现在看起来像这样:
sourceSets {
[flavor1, flavor2, flavor3 ...].each { AndroidSourceSet ss ->
ss.assets.srcDirs = ['repo-assets/' + ss.name.split('_')[0]]
ss.res.srcDirs = ['repo-mipmap/' + ss.name.split('_')[0] , 'repo-strings/' + ss.name]
}
}
也想为 productFlavors
做同样的事情,如上所示。
STUCK AT: 从外部来源获取上面代码中的列表 [flavor2, flavor4]
(以及上面列出的每种风格的一些附加字段)。
我看到像
这样的方法
productFlavors.add()
productFlavors.addAll()
但我不太确定如何使用这些。由于方法可用,我确信可以做我想做的事。
有没有人做过这个并且有一些指示?
对于阅读 XML 和 CSV 文件,您拥有 Groovy 的全部能力。所有 Gradle 脚本都应该用 Groovy 编写。 .each { Type var ->
也是其中的一部分。第一个结果::
给定一个像这样的 CSV 文件:
#name,appId,ad1,ad2,category
flavor1,app.id.flavor1,adunit1324523,adunit2234234,messenger
flavor2,app.id.flavor2,adunit42346451,adunit4562,editor
flavor3,app.id.flavor2.gpe,adunit345351,adunit3545342,messenger
Groovy可以这样加载:
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.internal.dsl.ProductFlavor
// TODO get a real CSV parser, this is hacky
new File("flavors.csv").splitEachLine(",") { fields ->
if (fields[0].charAt(0) == '#' as char) return; // skip comments
def flavorName = fields[0];
def baseName = flavorName.split('_')[0];
def appId = fields[1];
BaseExtension android = project.android // project.getExtensions().getByName('android');
// productFlavors is declared as Collection, but it is a NamedDomainObjectContainer
// if [flavorName] doesn't work, try .maybeCreate(flavorName) or .create(flavorName)
ProductFlavor flavor = android.productFlavors[flavorName];
AndroidSourceSet sourceSet = android.sourceSets[flavorName];
flavor.applicationId = appId;
sourceSet.res.srcDirs = [] // clear
sourceSet.res.srcDir 'repo-mipmap/' + baseName
sourceSet.res.srcDir 'repo-strings/' + flavorName
}
类型是为了可读性和代码完成而导入的,您可以用 def
替换任何变量类型,它仍然有效。这些类型正是您在进行常规 android { ... }
配置时所使用的类型。内部类型可能随时更改,事实上我正在使用 1.5,它们可能已经在 2.0 中更改。
我终于让它以这种方式工作了:
在 build.gradle
中创建了自定义 class MyFlavor
,并将 csv 文件中的每种口味添加到 ArrayList
的 MyFlavor
class MyFlavor {
public String flavorname;
public String basename;
public String appid;
public String bannerid;
public String interstitialid;
public String category;
}
def ArrayList<MyFlavor> myFlavors = new ArrayList<>();
new File('app/flavors.csv').eachLine {
String[] values = "$it".split(',');
MyFlavor f = new MyFlavor();
f.flavorname = values[0];
f.basename = values[0].split('_')[0];
f.appid = values[1];
f.bannerid = values[2];
f.interstitialid = values[3];
if(values[0].contains('_')) f.category= "state"
else f.category = "country";
myFlavors.add(f);
}
然后遍历 ArrayList
以动态创建 productFlavors
和 sourceSets
,如下所示:
productFlavors {
myFlavors.each { MyFlavor f ->
"$f.flavorname" {
applicationId = "$f.appid"
buildConfigField 'String', 'BANNER_ID', "\"" + "$f.bannerid" + "\""
buildConfigField 'String', 'INTERSTITIAL_ID', "\"" + "$f.interstitialid" + "\""
buildConfigField 'String', 'CATEGORY', "\"" + "$f.category" + "\""
}
}
}
sourceSets {
myFlavors.each { MyFlavor f ->
"$f.flavorname" {
assets.srcDirs = ['repo-assets/' + "$f.basename"]
res.srcDirs = ['repo-mipmap/' + "$f.basename" , 'repo-strings/' + "$f.flavorname"]
}
}
}
希望这对某人有所帮助。我成功地摆脱了 1000 行代码(因为我有很多口味)并将其减少到你在这里看到的这 10 行代码。
这个问题是
我可以使用以下代码在 sourceSets
下对口味(具有通用配置)进行分组:
(从上面链接问题中的天才那里得到的)
import com.android.build.gradle.api.AndroidSourceSet
android {
sourceSets {
[flavor2, flavor4].each { AndroidSourceSet ss ->
ss.assets.srcDirs = ['repo-assets/flavor2']
ss.res.srcDirs = ['repo-res/flavor2']
}
}
}
现在,我想知道是否可以从以下任何一项中提取列表 [flavor2, flavor4]
:
- 一个 XML 文件,我可以对其进行迭代以获得所有口味(我将把它放在那里)
- 我可以在其上迭代和获取值的 CSV 文件。
- 自定义 class,我可以将其写入单独的文件并从 class 中的静态成员获取数据。
除了风味名称外,我打算将以下内容存储在外部源(以上之一)中:
- 应用程序 ID(我将拉到
productFlavors
) - 广告单元 ID(每种口味两个)
- 其他一些自定义值,如类别等
PORPOSE: 我想编写一段通用代码来迭代和动态创建 productFlavors
和 sourceSets
。我已将 sourceSets
概括为将近 90%,现在一个街区足以满足所有口味。
现在看起来像这样:
sourceSets {
[flavor1, flavor2, flavor3 ...].each { AndroidSourceSet ss ->
ss.assets.srcDirs = ['repo-assets/' + ss.name.split('_')[0]]
ss.res.srcDirs = ['repo-mipmap/' + ss.name.split('_')[0] , 'repo-strings/' + ss.name]
}
}
也想为 productFlavors
做同样的事情,如上所示。
STUCK AT: 从外部来源获取上面代码中的列表 [flavor2, flavor4]
(以及上面列出的每种风格的一些附加字段)。
我看到像
这样的方法productFlavors.add()
productFlavors.addAll()
但我不太确定如何使用这些。由于方法可用,我确信可以做我想做的事。
有没有人做过这个并且有一些指示?
对于阅读 XML 和 CSV 文件,您拥有 Groovy 的全部能力。所有 Gradle 脚本都应该用 Groovy 编写。 .each { Type var ->
也是其中的一部分。第一个结果::
给定一个像这样的 CSV 文件:
#name,appId,ad1,ad2,category
flavor1,app.id.flavor1,adunit1324523,adunit2234234,messenger
flavor2,app.id.flavor2,adunit42346451,adunit4562,editor
flavor3,app.id.flavor2.gpe,adunit345351,adunit3545342,messenger
Groovy可以这样加载:
import com.android.build.gradle.BaseExtension
import com.android.build.gradle.api.AndroidSourceSet
import com.android.build.gradle.internal.dsl.ProductFlavor
// TODO get a real CSV parser, this is hacky
new File("flavors.csv").splitEachLine(",") { fields ->
if (fields[0].charAt(0) == '#' as char) return; // skip comments
def flavorName = fields[0];
def baseName = flavorName.split('_')[0];
def appId = fields[1];
BaseExtension android = project.android // project.getExtensions().getByName('android');
// productFlavors is declared as Collection, but it is a NamedDomainObjectContainer
// if [flavorName] doesn't work, try .maybeCreate(flavorName) or .create(flavorName)
ProductFlavor flavor = android.productFlavors[flavorName];
AndroidSourceSet sourceSet = android.sourceSets[flavorName];
flavor.applicationId = appId;
sourceSet.res.srcDirs = [] // clear
sourceSet.res.srcDir 'repo-mipmap/' + baseName
sourceSet.res.srcDir 'repo-strings/' + flavorName
}
类型是为了可读性和代码完成而导入的,您可以用 def
替换任何变量类型,它仍然有效。这些类型正是您在进行常规 android { ... }
配置时所使用的类型。内部类型可能随时更改,事实上我正在使用 1.5,它们可能已经在 2.0 中更改。
我终于让它以这种方式工作了:
在 build.gradle
中创建了自定义 class MyFlavor
,并将 csv 文件中的每种口味添加到 ArrayList
的 MyFlavor
class MyFlavor {
public String flavorname;
public String basename;
public String appid;
public String bannerid;
public String interstitialid;
public String category;
}
def ArrayList<MyFlavor> myFlavors = new ArrayList<>();
new File('app/flavors.csv').eachLine {
String[] values = "$it".split(',');
MyFlavor f = new MyFlavor();
f.flavorname = values[0];
f.basename = values[0].split('_')[0];
f.appid = values[1];
f.bannerid = values[2];
f.interstitialid = values[3];
if(values[0].contains('_')) f.category= "state"
else f.category = "country";
myFlavors.add(f);
}
然后遍历 ArrayList
以动态创建 productFlavors
和 sourceSets
,如下所示:
productFlavors {
myFlavors.each { MyFlavor f ->
"$f.flavorname" {
applicationId = "$f.appid"
buildConfigField 'String', 'BANNER_ID', "\"" + "$f.bannerid" + "\""
buildConfigField 'String', 'INTERSTITIAL_ID', "\"" + "$f.interstitialid" + "\""
buildConfigField 'String', 'CATEGORY', "\"" + "$f.category" + "\""
}
}
}
sourceSets {
myFlavors.each { MyFlavor f ->
"$f.flavorname" {
assets.srcDirs = ['repo-assets/' + "$f.basename"]
res.srcDirs = ['repo-mipmap/' + "$f.basename" , 'repo-strings/' + "$f.flavorname"]
}
}
}
希望这对某人有所帮助。我成功地摆脱了 1000 行代码(因为我有很多口味)并将其减少到你在这里看到的这 10 行代码。