【问题标题】:Different app names for different build flavors?不同构建风格的不同应用名称?
【发布时间】:2013-11-07 08:21:34
【问题描述】:

我有 2 种构建风格,例如,flavor1flavor2

我希望我的应用程序在为风味 1 构建时命名为“AppFlavor1”,在为风味 2 构建时命名为“AppFlavor2”。

这不是我要更改的活动标题。我想更改显示在手机菜单和其他地方的应用名称。

来自build.gradle,我可以为我的口味设置各种参数,但似乎不是应用程序标签。而且我也不能基于某些变量以编程方式更改应用标签。

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

【问题讨论】:

    标签: android gradle


    【解决方案1】:

    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 或编写自定义脚本的破坏性要小。

    【讨论】:

    • 这就像一个魅力,但问题是:在添加这样的字符串资源时,我们如何为其他语言环境添加本地化字符串?
    • 在这里发布了一个问题:stackoverflow.com/questions/36105494/…你介意看看吗?
    • 使用多个风味维度怎么样?如何为每种风味/风味组合分别命名?
    • 一切都很好,但是显示在启动器图标下方的应用程序名称并没有按照 gradle 产品风格的设置进行更改。我从 strings.xml 中删除了“app_name”并设置了产品风味。如果我从 android 的应用信息页面看到应用名称,显示按产品风格设置的应用名称,但在启动器图标下显示我以前的应用名称。
    • @mahbub.kuet 您是否使用@string/app_name 作为您的启动器activity 标签?请记住,主启动器 activity 中的 label 优先用于应用显示名称的 application label
    【解决方案2】:

    与其使用脚本更改主要的 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 添加到数组中,而不是完全重新定义它。
    • 是的,我现在也想通了。非常感谢
    • 应该在哪里添加sourceSets 块?是在android{...} 块下吗?
    • @AbelMelquiadesCallejo 确实在 android 块内。
    【解决方案3】:

    如果您想保持不同风格的应用名称的本地化,那么您可以通过以下方式实现:

    1) 在&lt;application&gt; 中指定android:labelAndroidManifest.xml 中可用,如下所示:

    <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)添加字符串资源。这将允许您对它们进行本地化。

    【讨论】:

    • 第 2 步必须在 build.gradle 的 'defaultConfig' 级别,而不是在 'android' 级别,否则您将收到与 manifestPlaceholders 名称相关的未知属性错误。
    • 为了实现这一点,我还必须在 &lt;application&gt; 元素中添加 tools:replace="android:label" 属性。
    【解决方案4】:

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

    文件结构:

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

    【讨论】:

    • 这比在 gradle 文件中声明要好。奇迹般有效。谢谢。
    【解决方案5】:

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

    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>
    

    编译器将对清单进行合并,您可以为每种风格设置一个图标。

    【讨论】:

      【解决方案6】:

      这可以在 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

      【讨论】:

        【解决方案7】:

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

        我使用一个 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>
        

        【讨论】:

        • 这是一种 Light vs Pro 版本,我认为用户很可能同时拥有它们
        • 那么你将不得不更改应用程序包名称,例如com.example.mysoft.pro vs com.example.mysoft.light,否则用户将只能在同一设备上安装其中一个。
        • 我这样做了,但是如何在手机菜单中获得“我的应用程序”与“我的应用程序专业版”?我现在有 2 个应用程序(light 和 pro),但它们的名称相同。为此,我确实需要 2 个不同的应用名称,也就是说,正如您所说,不是那么容易吗?
        • 正如另一个(被否决的)答案提到的那样,在 AndroidManifest.xml 中有&lt;application android:icon="@drawable/ic_launcher" android:label="@string/app_name" &gt;。在您清理并重新构建所有内容后(两次,每种口味一次),您应该获得两个具有不同 android 包名称和不同启动器标签的 apk-s。顺便说一句,Eclipse 经常不会检测到资源的变化,所以你必须要求它清理。
        • 我如何使字符串/app_name 因口味而异?很抱歉,我仍然无法得到它。
        【解决方案8】:

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

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

        Python 脚本有一个参数,一个目录名。该目录包含每种风格的资产、启动器图标等资源以及带有 Python 字典的文件 properties.txt。

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

        Python 脚本从该文件加载字典并将&lt;string name="app_name"&gt;&lt;/string&gt; 之间的值替换为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
        

        【讨论】:

          【解决方案9】:

          在 AndroidManifest 文件中,在应用程序标签中你有这行:

          android:label
          

          您可以在此处说明如何在设备的应用程序菜单中显示应用程序标签

          【讨论】:

          • 是的,我读到了这个问题...您可以设置 2 个分支,并为每个分支(一个用于 flavor1,一个用于 flavor2)在您的 AndroidManifest 文件中设置不同的 android:label。
          • 啊,我不知道。我如何放置这 2 个清单文件呢?我虽然不可能
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2014-02-13
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-10-27
          • 2015-07-21
          • 2015-09-10
          相关资源
          最近更新 更多