Different app names for different build flavors?

I have 2 build flavors, say, flavor1 and flavor2.

I would like my application to be named, say, "AppFlavor1" when I build for flavor1 and "AppFlavor2" when I build for flavor 2.

It is not the title of activities I want to change. I want to change the app name as it's displayed on the phone menu and elsewhere.

From build.gradle I can set various parameters for my flavors but, it seems, not the app label. And I can not change the app label programmatically based on some variable, too.

So, how do people handle this?

46825 次浏览

首先,回答这个问题: “用户可以在同一个设备上同时安装应用程序的两种风格吗?”

我使用一个 Python 脚本来修补源代码。 它包含一些可重用的函数,当然,还有在这个特殊项目中需要修补的知识。因此脚本是特定于应用程序的。

有很多补丁,用于补丁的数据保存在一个 Python 字典中(包括应用程序包名称,它们 BTW 不同于 Java 包名称) ,每种风格一个字典。

至于 l10n,字符串可能指向其他字符串,例如,在我的代码中,我有:

<string name="app_name">@string/x_app_name_xyz</string>


<string name="x_app_name_default">My Application</string>
<string name="x_app_name_xyz">My App</string>

在 AndroidManifest 文件中,在 application 标记中有以下一行:

android:label

在那里你可以说,如何应用标签将出现在设备上的应用菜单

How do I make string/app_name different per flavor though?

我想写一个更新,但是意识到它比原来的答案要大,原来的答案是我使用一个 Python 脚本来补丁源代码。

The Python script has a parameter, a directory name. That directory contains per-flavor assets, resources like launcher icons, and the file properties.txt with a Python dictionary.

{ 'someBoolean' : True
, 'someParam' : 'none'
, 'appTitle' : '@string/x_app_name_xyz'
}

Python 脚本从该文件加载字典,并将 <string name="app_name"></string>之间的值替换为 properties['appTitle']

下面的代码是在原样/原样的基础上提供的。

for strings_xml in glob.glob("res/values*/strings.xml"):
fileReplace(strings_xml,'<string name="app_name">',properties['appTitle'],'</string>',oldtextpattern=r"[a-zA-Z0-9_/@\- ]+")

从一个或多个此类文件中读取属性:

with open(filename1) as f:
properties = eval(f.read())
with open(filename2) as f:
properties.update(eval(f.read()))

FileReplace 函数是:

really = True
#False for debugging


# In the file 'fname',
# find the text matching "before oldtext after" (all occurrences) and
# replace 'oldtext' with 'newtext' (all occurrences).
# If 'mandatory' is true, raise an exception if no replacements were made.
def fileReplace(fname,before,newtext,after,oldtextpattern=r"[\w.]+",mandatory=True):
with open(fname, 'r+') as f:
read_data = f.read()
pattern = r"("+re.escape(before)+r")"+oldtextpattern+"("+re.escape(after)+r")"
replacement = r"\g<1>"+newtext+r"\g<2>"
new_data,replacements_made = re.subn(pattern,replacement,read_data,flags=re.MULTILINE)
if replacements_made and really:
f.seek(0)
f.truncate()
f.write(new_data)
if verbose:
print "patching ",fname," (",replacements_made," occurrence" + ("s" if 1!=replacements_made else ""),")",newtext,("-- no changes" if new_data==read_data else "-- ***CHANGED***")
elif replacements_made:
print fname,":"
print new_data
elif mandatory:
raise Exception("cannot patch the file: "+fname+" with ["+newtext+"] instead of '"+before+"{"+oldtextpattern+"}"+after+"'")

剧本的第一行是:

#!/usr/bin/python
# coding: utf-8


import sys
import os
import re
import os.path
import shutil
import argparse
import string
import glob
from myutils import copytreeover

与其用脚本改变你的主 strings.xml 并且冒着弄乱源代码控制的风险,为什么不依赖于 Android Gradle 构建的标准合并行为呢?

我的 build.gradle包含

sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
renderscript.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
}


release {
res.srcDir 'variants/release/res'
}


debug {
res.srcDir 'variants/debug/res'
}
}

所以现在我可以定义我的 app_name字符串在 variants/[release|debug]/res/strings.xml。和任何其他我想改变,也!

您可以向每种风格添加一个字符串资源文件,然后使用这些资源文件来更改您的应用程序名称。 例如,在我的一个应用程序中,我有一个免费和付费的版本。为了将它们重命名为“ Lite”和“ Pro”,我创建了一个 meta_data.xml文件,并将我的 app_name值添加到该 XML 中,然后将其从 strings.xml中删除。 接下来,在 app/src中为每种口味创建一个文件夹(参见下面的例子结构)。在这些目录中,添加 res/values/<string resource file name>。现在,当您构建时,这个文件将被复制到您的构建中,并且您的应用程序将被重命名。

文件结构:

app/src
/pro/res/values/meta_data.xml
/lite/res/values/meta_data.xml

strings.xml中删除 app_name(else gradle 将报告重复的资源)。然后像下面这样修改构建文件:

productFlavors {
flavor1{
resValue "string", "app_name", "AppNameFlavor1"
}


flavor2{
resValue "string", "app_name", "AppNameFlavor2"
}
}

还要确保为清单中的 android:label属性分配了 @string/app_name值。

<application
...
android:label="@string/app_name"
...

这比在不同的构建集合下创建新的 strings.xml或编写自定义脚本具有更小的破坏性。

Another option that I actually use is change the manifest for each application. Instead of copy the resource folder, you can create a manifest for each flavour.

sourceSets {
main {
}


release {
manifest.srcFile 'src/release/AndroidManifest.xml'
}


debug {
manifest.srcFile 'src/debug/AndroidManifest.xml'
}
}

您必须在 src main 中有一个主体 AndroidManifest,它将成为主体。然后您可以为每种风味定义一个只有一些选项的清单,比如(src/release/AndroidManifest.xml) :

<manifest package="com.application.yourapp">
<application android:icon="@drawable/ic_launcher">
</application>
</manifest>

对于调试,AndroidManifest (src/debug/AndroidManifest.xml) :

<manifest package="com.application.yourapp">
<application android:icon="@drawable/ic_launcher2">
</application>
</manifest>

Compiler will do a merge of the manifest and you can have a icon for each flavour.

这可以很容易地在 buildType 下完成

buildTypes {
debug {
buildConfigField("String", "server_type", "\"TEST\"")
resValue "string", "app_name", "Eventful-Test"
debuggable true
signingConfig signingConfigs.debug_key_sign
}


stage {
buildConfigField("String", "server_type", "\"STAGE\"")
resValue "string", "app_name", "Eventful-Stage"
debuggable true
signingConfig signingConfigs.debug_key_sign
}


release {
buildConfigField("String", "server_type", "\"PROD\"")
resValue "string", "app_name", "Eventful"
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
//TODO - add release signing
}
}

只要确保从 strings.xml 中删除 app _ name

如果你想为应用程序的名称保持不同风格的本地化,那么你可以这样做:

1)在 <application>中指定 AndroidManifest.xml中的 android:label如下:

<application
...
android:label="${appLabel}"
...
>

2)在应用程序级别 build.gradle中指定 appLabel的默认值:

manifestPlaceholders = [appLabel:"@string/defaultName"]

3)重写产品风味的价值如下:

productFlavors {
AppFlavor1 {
manifestPlaceholders = [appLabel:"@string/flavor1"]
}
AppFlavor2 {
manifestPlaceholders = [appLabel:"@string/flavor2"]
}


}

4)在 strings.xml中为每个字符串添加字符串资源(defaultName、 Flavor1、 Flavor2)。这将允许您对它们进行本地化。

这非常容易实现。如果你已经在你的应用程序中创建了风味,如果应用程序名称来自 AndroidManifest.xml。这么说吧

<application
android:name="com.prakash.sampleapp"
android:label="@string/app_name">

你所要做的就是为你的口味创建 string/app_name

  1. 切换到 Android 工作室的“项目”窗格。 enter image description here
  2. 右键单击 app/src并选择 New>XML>Values XML File
  3. 选择适当的 Target Source Set并创建 strings.xml文件。enter image description here
  4. 根据您的喜好更新 app_name
  5. 安装风味 ./gradlew installFlavorNameDebug,你会看到应用程序的更新名称。

you could also create strings.xml manually but AS makes it easier to create all directories and resources files for you with above method.