【问题标题】:Android – multiple custom versions of the same appAndroid – 同一应用的多个自定义版本
【发布时间】:2010-11-16 09:02:28
【问题描述】:

部署多个自定义版本的 Android 应用程序的最佳方式是什么?

目前我有一个脚本来交换资源文件夹以获取我的应用程序的自定义版本。它工作得很好,但所有自定义版本在 AndroidManifest.xml 中仍然具有相同的包名称。因此,无法同时安装两个自定义版本的应用。

This is one solution for this problem, but that has to be done by hand

您能想出一个更简单的解决方案,或者如何将其构建到脚本中?

(顺便说一句:它不适用于色情/垃圾邮件/任何应用程序,甚至不是付费应用程序)

【问题讨论】:

  • 我已经在这里回答了类似的问题,也许它也可以帮助你:stackoverflow.com/questions/16840127/…
  • 接受的答案有点过时了。有一个由起重机发布的,这可能是现在正确的做法。
  • 肯定想要在 Android Studio 上使用原生的 Gradle flavors,甚至受到鼓励。

标签: android automation


【解决方案1】:

也许内置的 Android“库”概念在最初发布时还没有完全成熟,但它可能是 2011 年的首选方法。按照以下步骤构建 ant:

从一个正在运行的应用程序开始(我们称之为目录“myOrigApp”,包com.foo.myapp),只需将此行添加到“default.properties”以使其成为一个库:

android.library=true

现在以您喜欢的任何方式在同级目录中创建一个新应用程序(我们将其称为“同级”目录,包 com.foo.myVariant)。例如,使用 Intellij Idea,使用目录“sibling”创建一个“从头开始”项目,它将创建您通常需要的所有文件/目录。

在新的同级目录中编辑“default.properties”以添加依赖项:

android.library.reference.1=../myOrigApp

从原始目录复制清单:

cd sibling
cp ../myOrigApp/AndroidManifest.xml  ../myOrigApp/local.properties ../myOrigApp/build.properties  .

编辑复制的清单文件以将其包名称更改为您的新变体“com.foo.myVarient”;这是唯一的变化。

如果您只运行 ant 构建脚本,您可能会完成。 (我只需要设置签名密钥。)

如果您想设置像 Idea 这样的 IDE 以将库项目作为变体项目的依赖项,请按照以下步骤将库项目添加到变体项目(假设您已经为这两个项目设置了项目) :

  • 打开原始项目,调出项目设置,选择您的 Facet 并勾选“Is Library Project”并保存。
  • 打开变体项目,调出项目设置,选择模块
  • 添加模块
  • 选择“导入现有模块”
  • 浏览至原始目录 (myOrigApp) 并选择其 .iml 文件(IntelliJ 项目源文件)
  • 单击“完成”。 (库项目作为模块添加到变体项目中。)
  • 在模块列表中单击 Variant 项目以将其选中。
  • 在右侧选择“依赖项”选项卡。
  • 点击“添加...”
  • 选择“模块依赖项...”(应该会出现一个列表,其中包含您之前添加到项目中的模块/库的名称——可能是列表中的唯一条目)。
  • 选择您添加的库项目,然后按确定。 (它将被添加到您项目的依赖项列表中。)
  • 按 OK 完成项目配置。 (您应该会看到 2 个模块,其中库的资源和类在 Variant 项目中可用并被识别。)

【讨论】:

  • 创意步骤归功于soledadpenades.com/2011/06/17/…
  • 我相信这个解决方案当时并不存在,但它肯定比使用正则表达式浏览源文件要好。我之前一直在使用Prashast 的解决方案,效果很好。但对于新项目,我更喜欢图书馆项目。
  • 我更喜欢这个解决方案,而不是修改几十个源文件(源代码管理的噩梦!)。
  • 我相信这种方法有一个主要的限制,如果你在你的主应用程序中使用资产或其他一些类型(?)的资源,当你把它变成一个 jar 库时,它们不会是可用于尝试使用该库的衍生版本。如果您只是想共享代码和/或布局,没问题。但是,如果您的应用程序使用资产,那么我认为这会发生故障(没有一些自定义的 aapt 构建脚本)。
  • 你是对的@Nate。来自文档:这些工具不支持在库项目中使用原始资产文件(保存在 assets/ 目录中)。应用程序使用的任何资产资源都必须存储在应用程序项目本身的 assets/ 目录中。见developer.android.com/guide/developing/projects/index.html
【解决方案2】:

我为类似的事情所做的只是使用一个 antlib 任务,然后遍历所有 java 和 xml 文件以将我的旧包字符串替换为新的包字符串。根据包,文件是否不在正确的 src 路径中并不重要。只需对所有文件进行正则表达式替换就足以让我完成这项工作......

例如在src目录下的所有java文件中替换它:

 <replaceregexp flags="g" byline="false">
    <regexp pattern="old.package.string" /> 
    <substitution expression="new.package.string" />
    <fileset dir="src" includes="**/*.java" /> 
 </replaceregexp>

【讨论】:

  • 这解决了我的问题,谢谢。我现在使用两个replaceregexp。一个与您的完全相同,另一个用于替换 AndroidManifest.xml 中的包名称。
  • 对我不起作用,因为所有源文件仍在与旧包名称匹配的目录结构中。你是怎么解决这个问题的?
  • 对我来说源文件的位置并不重要。只要他们有正确的包裹信息。但是在 Eclipse 中打开它可能不起作用。我只在 ant 构建和使用生成的 apk 文件时使用了它。
  • @Prashast 我试过了,(1)创建 build.xml (2)放了 但没有成功。你会分享一个示例 android 应用程序的完整 build.xml 吗?问候...
【解决方案3】:

肯定想在 Android Studio 上使用本机自带的 Gradle flavors,甚至受到鼓励。

它似乎很好地解释了所有基础知识。我今天刚完成转换为Gradle,效果很好。自定义应用图标、名称和字符串等。

正如该网站所解释的,此设计背后的部分目的是使其更具动态性,并更轻松地允许使用基本相同的代码创建多个 APK,这听起来与您正在做的事情相似。

我可能没有解释得最好,但是那个网站做得很好。

【讨论】:

  • 这是现在要走的路。 +1
【解决方案4】:

链接到的解决方案不必手动完成。请记住,&lt;manifest&gt; 元素中的 package 属性不必是代码所在的位置,只要您在清单的其他地方拼出完全限定的类(例如,activity android:name="com.commonsware.android.MyActivity" 而不是 @987654324 @)。编写清单更改脚本并使用 Ant 构建新的 APK。 AFAIK,应该可以。

【讨论】:

  • 这会破坏自动生成的 R 文件。因此,您必须或多或少地检查所有源文件并更改 R 文件的导入(第 8 点)。
  • 我们解决了 R 问题。导入 old.package.string.*;导入新的.package.string.*;这只是为两个构建导入 R。
【解决方案5】:

支持多个合作伙伴 准备 config.xml

为不同的合作伙伴构建项目

<!--partner.dir, pkg.name, ver.code, ver.name are input from command line when execute 'ant' -->

<!-- set global properties for this build -->
<property name="build.bin" location="bin"/>
<property name="build.gen" location="gen"/>
<property name="src" location="src"/>
<property name="res" location="res"/>

<target name="preparefiles" description="Prepare files for different partner" >
    <delete dir="${build.bin}" />
    <delete dir="${build.gen}" />

    <copy todir="${res}" overwrite="true" />
        <fileset dir="${partner.dir}/res" /> 
    </copy>

    <!-- change the import in all Java source files -->
    <replaceregexp file="AndroidManifest.xml"
        match='android.versionCode="(.*)"'
        replace='android.versionCode="${ver.code}"'
        byline="false">

    <replaceregexp file="AndroidManifest.xml"
        match='android.versionName="(.*)"'
        replace='android.versionName="${ver.name}"'
        byline="false">

    <replaceregexp file="AndroidManifest.xml"
        match='package="(.*)"'
        replace='package="${pkg.name}"'
        byline="false">

    <!-- change the package name in AndroidManifest -->
    <replaceregexp flags="g" byline="false">
        <regexp pattern="import(.*)com.myproject.com.R;" /> 
        <substitution expression="import com.${pkg.name}.R;" />
        <fileset dir="${src}" includes="**/*.java" /> 
    </replaceregexp>

    <replaceregexp flags="g" byline="false">
        <regexp pattern="(package com.myproject.com;)" /> 
        <substitution expression="\1&#10;import com.${pkg.name}.R;" />
        <fileset dir="${src}" includes="**/*.java" /> 
    </replaceregexp>
</target>

准备文件 $ ant -f config.xml -Dpartner.dir="xxx" -Dpkg.name="xxx" -Dver.code="xxx" -Dver.name="xxx" 准备文件

创建 build.xml 建造 $蚂蚁调试 要么 $蚂蚁释放

【讨论】:

  • 如果您只使用 ant,这很好。如果你在 Eclipse 中工作,在运行正则表达式后你可以工作,但如果你更改资源,你会再次卡住。
  • 该文件有一些小问题,但总的来说是个好主意!我复制了它并对其进行了一些修改,以便在客户项目中使用。谢谢!
【解决方案6】:

我是using the maven-android-plugin 来实现这一目标。为生成的源目标指定一个 AndroidManifest.xml,为最终的 apk 目标指定另一个 AndroidManifest.xml。这样源代码项目在 R 类生成和构建阶段保留了实际的源代码包名称,而市场适应的清单包名称在第二个 AndroidManifest.xml 中,它包含在最终的 apk 文件中。

【讨论】:

    【解决方案7】:

    我最终得到了一个修补源代码的脚本;修补源代码听起来有风险,但在存在版本控制的情况下,风险是可以接受的。

    所以我制作了一个版本,提交了源代码,制作了另一个版本,提交了源代码,然后查看了差异,用 Python 编写了一个补丁脚本。

    我不确定这是否是最佳解决方案。 (而且代码遗漏了一些 os.path.joins)

    脚本的核心是以下函数:

    # 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,mandatory=True):
        with open(fname, 'r+') as f:
        read_data = f.read()
        pattern = r"("+re.escape(before)+r")\w+("+re.escape(after)+r")"
        replacement = r"\1"+newtext+r"\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 "",")"
        elif replacements_made:
            print fname,":"
            print new_data
        elif mandatory:
            raise Exception("cannot patch the file: "+fname)
    

    您可能会发现以下用途之一:

    # Change the application resource package name everywhere in the src/ tree.
    # Yes, this changes the java files. We hope that if something goes wrong,
    # the version control will save us.
    def patchResourcePackageNameInSrc(pname):
        for root, dirs, files in os.walk('src'):
        if '.svn' in dirs:
            dirs.remove('.svn')
        for fname in files:
            fileReplace(os.path.join(root,fname),"import com.xyz.",pname,".R;",mandatory=False)
    

    还有一个功能可以将资产从x-assets-cfgname复制到assets(之前发现对我来说在assets中有一个子目录更方便)。

    def copyAssets(vname,force=False):
        assets_source = "x-assets-"+vname+"/xxx"
        assets_target = "assets/xxx"
        if not os.path.exists(assets_source):
            raise Exception("Invalid variant name: "+vname+" (the assets directory "+assets_source+" does not exist)")
        if os.path.exists(assets_target+"/.svn"):
            raise Exception("The assets directory must not be under version control! "+assets_target+"/.svn exists!")
        if os.path.exists(assets_target):
            shutil.rmtree(assets_target)
        shutil.copytree(assets_source, assets_target, ignore=shutil.ignore_patterns('.svn'))
    

    嗯,你明白了。现在您可以编写自己的脚本了。

    【讨论】:

      【解决方案8】:

      我认为最好的方法是创建一个新项目并复制这些东西。脚步, - 创建没有类的新 android 项目 - 创建包(包名应该与清单文件中的对应),或者只是复制'gen'文件夹中的包名 - 复制java文件 - 复制可绘制文件夹 - 复制布局文件 - 复制您项目中使用的任何其他文件 - 复制清单文件的数据

      这对我来说更简单

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-02-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多