【发布时间】:2011-09-15 08:25:10
【问题描述】:
我遇到了问题。我需要以两种方式构建一个应用程序,第一个构建用于开发(测试)使用,第二个构建应该是生产版本。有什么方法可以以编程方式进行吗? (使用一些构建引擎)我的意思是如果可能的话,两个应用程序应该同时在一台设备上运行。两个版本都是来自一个 Android 项目的 APK。
谢谢
【问题讨论】:
我遇到了问题。我需要以两种方式构建一个应用程序,第一个构建用于开发(测试)使用,第二个构建应该是生产版本。有什么方法可以以编程方式进行吗? (使用一些构建引擎)我的意思是如果可能的话,两个应用程序应该同时在一台设备上运行。两个版本都是来自一个 Android 项目的 APK。
谢谢
【问题讨论】:
我个人用这个来判断我是否处于调试模式:
final PackageInfo pinfo = getPackageInfo(ctx);
final boolean debugMode = (pinfo.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
此代码基于android-manifest.xml 的Application 标记的debuggable 属性:
如果此属性显式设置为true,debugMode 将设置为true。
但如果它被明确设置为false 或不存在于 xml(隐式值)中,debugMode 将被设置为false。
这样做您不能同时在同一设备上运行两个应用程序,因为两个 APK 需要同时安装两个不同的包名称。所以你必须构建两个 Eclipse 项目,每个项目都有自己的包名(例如com.example.myapp.debug 和com.example.myapp),为什么不使用一个包含几乎所有代码的通用库(com.example.myapp.common):
com.example.myapp.debug 的debuggable 标志设置为true
并且com.example.myapp 的debuggable 标志设置为false
【讨论】:
boolean debuggable = (0 != (context.getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
据我所知,您确实需要根据基本代码创建不同的应用程序。正如我所做的那样,完成此操作的一种方法是使用 Ant 脚本将整个项目源复制到另一个目录中,例如“测试”,并在这样做的同时替换(例如使用复制过滤)来自 XML 文件的某些值,就像来自 AndroidManifest.xml 一样。首先要替换的东西之一是应用程序包,它需要对每个应用程序都是唯一的。像Activities 这样的Java 类仍然可以驻留在原始包中,它们在AndroidManifest.xml 中的名称只需要是绝对的。一旦源被复制和过滤,您就可以使用来自主 build.xml 的 Ant 的 antcall 任务来构建定制的应用程序。所以最后,你可以说,例如:“ant -Denv=testing build”,你就有了一个可以安装在你的生产版本旁边的 APK。
另一种选择是使用 Maven,它的 Android 插件支持项目覆盖。当然,您也可以使用库项目,请参阅:Android – multiple custom versions of the same app。
【讨论】:
我认为最简单的解决方案是为此目的使用某种源代码控制工具。使用源代码管理有很多充分的理由,我相信大多数开发人员已经在使用它。
解决方案总结:
解决方案说明。
我个人使用 GIT,我相信这种方法也适用于其他 SCM 工具,但我没有对其进行测试。
我有 2 个存储库,一个用于开发,一个用于生产(您可以使用生产分支获得相同的效果,但我更喜欢不同的存储库,因为我不知道什么时候会有其他开发人员,而且我不知道不想让任何人(包括我)在没有备份的情况下对代码犯错。
您只需在每个仓库的清单文件中设置不同的包名,例如:
生产清单包名称 - com.foo.appName
对于每个活动,都需要使用绝对路径而不是相对方法。由于您没有真正的选择来更改您的包名称,如果您这样做,所有更改都在清单文件中,我认为这种方法几乎没有任何缺点。
然后,每次您将更改从开发人员存储库拉到生产存储库时,清单文件中的这些行都应该存在“冲突”,但实际上只会在第一次发生冲突您提取代码,然后合并工具会知道您在生产存储库中更喜欢哪一行。
使用这种方式一段时间后发现生成的R文件有问题。
问题: 正在生成 R 文件,其包名称在包属性的清单文件中定义。然后找不到所有对R文件的引用(源文件的包名与manifest文件中的包名不同)。
该问题有 3 个解决方案:
善: 这个解决方案是最强大的,我建议你使用它(虽然没有自己尝试过)。此解决方案背后的想法是将 R 文件生成为与清单中所述不同的类名。在清单中,包将是 dev.com.foo.appName,但 R 文件将生成到 com.foo.appName。 为了实现它,请关注this answer
坏人: 不要使用这个解决方案,它真的很糟糕,我在这里说明你可以避免它。在使用 R 文件的每个文件中,将导入添加到 R 文件中,包名称在清单中。这是一个非常糟糕的解决方案,因为你会输入很多不相关的代码,你需要在生产环境中更改它,并且对于每个新类你都需要记住添加它。
还有丑陋的: 最好不要使用此解决方案,因为它是一种 hack。此解决方案仅适用于资源没有大量更改的成熟应用程序。当您更改资源时,将再次生成 R 文件,然后将其生成为清单中的包名称。您需要做的就是将包名称(在清单中)更改为生产环境中的名称,清理项目,重新构建,然后将包名称更改回开发环境。然后 Eclipse 询问是否更改配置,而您选择不更改。这样,将存在 2 个 R 文件,一个具有开发包名称,一个具有生产包名称。由于在成熟的应用程序中没有太多的资源变化,你会偶尔这样做。您将无法忘记它,因为如果您更改资源,您将开始看到奇怪的错误。
【讨论】:
我知道问题迟到了,但我会回答的。
您可以使用 Gradle。
在 build.gradle 文件中,您可以像这样定义单独的buildTypes:
buildTypes {
release {
runProguard false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
test {
applicationIdSuffix ".test"
versionNameSuffix "t"
debuggable false
}
通过设置applicationIdSuffix 可以在一台设备上安装test和release构建
欲了解更多信息,请转到http://tools.android.com/tech-docs/new-build-system/user-guide
【讨论】: