不同的构建版本需要不同的应用程序名称?

136

我有两个构建变体,分别为flavor1flavor2

当我针对flavor1进行构建时,我希望我的应用程序被命名为"AppFlavor1",而在构建flavor2时,则命名为"AppFlavor2"。

我想要更改的不是活动的标题,而是应用程序在电话菜单和其他地方显示的名称。

build.gradle中,我可以为我的变体设置各种参数,但似乎无法更改应用程序标签。我也无法基于某些变量编程更改应用程序标签。

那么,人们如何处理这个问题呢?

11个回答

339

strings.xml中删除app_name(否则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或编写自定义脚本相比,这种方法对系统影响较小。


13
这个方法非常有效,但问题是:当我们像这样添加字符串资源时,如何为其他语言环境添加本地化字符串? - Viral Patel
@Kevin,不,它仍然可以。该应用程序仍在使用这种范例进行积极开发。 - Ganesh Krishnan
5
使用多个口味维度怎么样? 我如何为每个口味/口味组合命名不同的名称? - Zocket
1
一切都很好,但是应用程序名称在启动器图标下显示的并没有按照Gradle产品风格设置的方式改变。我从strings.xml中删除了“app_name”,并在产品风格中进行了设置。如果我从Android的应用信息页面查看应用程序名称,则显示由产品风格设置的应用程序名称,但在启动器图标下仍然显示我的先前应用程序名称。 - iamcrypticcoder
3
你是否在你的启动器活动标签中使用了@string/app_name?请注意,主启动器activity的标签优先于应用程序名称(application label)用于应用程序显示名称。 - user650881
显示剩余7条评论

34

为避免使用脚本更改您的主要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'
    }
}

现在我可以在variants/[release|debug]/res/strings.xml中定义我的app_name字符串。还可以改变我想要改变的任何其他内容!


使用res.srcDir将其添加到数组中,而不是完全重新定义它。 - Pierre-Luc Paour
是的,我现在也已经弄清楚了。非常感谢。 - Alexander Kulyakhtin
1
一个人应该在哪里添加sourceSets块?是在android{...}块下面吗? - Abel Callejo
@AbelMelquiadesCallejo确实在android块内。 - Pierre-Luc Paour

20
如果您想在不同风格的应用程序名称中保持本地化,则可以按以下方式实现:
1)在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中为每一个String(defaultName、flavor1和flavor2)添加字符串资源。这将允许你进行本地化。


第二步必须在 build.gradle 的 'defaultConfig' 级别中,而不是 'android' 级别中,否则您将会收到与 manifestPlaceholders 名称相关的未知属性错误。 - Brendon Whateley
为了实现这个,我不得不在<application>元素中也添加了tools:replace="android:label"属性。 - rineez

9
您可以为每个风味添加一个字符串资源文件,然后使用这些资源文件来更改您的应用程序名称。例如,在我的一个应用程序中,我有一个免费版本和付费版本。为了将它们重命名为“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

2
这比在 gradle 文件中声明要好得多。运转良好。谢谢。 - Filip Luchianenco
这是 Android 的做法。Flavor 的资源会覆盖 main 的资源。本地化按预期工作,例如 /pro/res/values-es/strings.xml。如果您想要一个回退,也可以在 main 的 strings.xml 中保留 app_name。应该是被接受的答案。 - sulai

8

另一个选项是为每个应用程序更改清单。而不是复制资源文件夹,可以为每个风味创建一个清单。

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>

编译器将合并清单文件,每个版本可以有自己的图标。

7
这可以在buildTypes下轻松实现。
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。


如何使用这种方法本地化应用程序名称? - sulai

2
首先,回答这个问题:“用户可以在同一设备上安装您应用的两种不同版本吗?”
我使用一个Python脚本来打补丁。它包含一些可重用的函数和特定项目中需要打补丁的知识。因此,该脚本是应用程序特定的。
有很多补丁,打补丁所需的数据保存在Python字典中(包括应用程序包名称,它们与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>

这有点像轻量版和专业版,我认为用户可能会同时拥有它们。 - Alexander Kulyakhtin
那么您将需要更改应用程序包名称,例如 com.example.mysoft.procom.example.mysoft.light ,否则用户只能在同一设备上安装其中之一。 - 18446744073709551615
我已经这样做了,但如何在手机菜单中获得“My App”和“My App Pro”?我现在有两个应用程序(轻量版和专业版),但两者都具有相同的名称。我确实需要两个不同的应用程序名称,而正如你所说,这并不容易? - Alexander Kulyakhtin
正如其他(被踩的)答案所提到的,在AndroidManifest.xml中有<application android:icon="@drawable/ic_launcher" android:label="@string/app_name" >。在你清理并重新构建所有内容后(每个口味一次),你应该得到两个具有不同Android包名称和不同启动器标签的apk文件。顺便说一句,Eclipse经常无法检测到资源的更改,因此您必须要求它进行清理。 - 18446744073709551615
我该如何使每个风味的字符串/应用程序名称不同?很抱歉,我还是无法理解。 - Alexander Kulyakhtin

1

这很容易实现。如果您已经在您的应用程序中创建了不同版本,并且应用程序名称是从AndroidManifest.xml获取的。假设

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

你只需要为你的口味创建string/app_name

  1. 在Android Studio中切换到项目面板。 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,您将看到应用程序的更新名称。

您也可以手动创建strings.xml,但使用上述方法,AS会更轻松地为您创建所有目录和资源文件。


0
你可以为特定的风味创建一个单独的strings.xml文件,并使用app_name键。例如,对于storybook风味,我们将有以下strings.xml文件。

enter image description here

在这种情况下,我们只需要在build.gradle中设置最小配置即可。

enter image description here


-1

如何使每个风味的字符串/应用程序名称不同?

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

Python脚本有一个参数,即目录名称。该目录包含每个风味的资源,例如启动器图标和文件properties.txt,其中包含一个Python字典。

{ '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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接