怎样部署多个定制版的Android应用程序是最佳方式?
目前,我有一个脚本来交换资源文件夹以获取我的应用程序的定制版本。它运行得很好,但所有定制版本在AndroidManifest.xml中仍然具有相同的包名。因此,无法同时安装两个定制版本的应用程序。
你能想到更简单的解决方案吗?或者如何将其构建到脚本中?
(顺便说一句:这不是用于色情/垃圾邮件/任何东西的应用程序,甚至不是付费应用程序)
怎样部署多个定制版的Android应用程序是最佳方式?
目前,我有一个脚本来交换资源文件夹以获取我的应用程序的定制版本。它运行得很好,但所有定制版本在AndroidManifest.xml中仍然具有相同的包名。因此,无法同时安装两个定制版本的应用程序。
你能想到更简单的解决方案吗?或者如何将其构建到脚本中?
(顺便说一句:这不是用于色情/垃圾邮件/任何东西的应用程序,甚至不是付费应用程序)
也许在最初的帖子发布时,Android内置的“库”概念并不完善,但从2011年开始它可能是首选的方法。按照以下步骤进行ant构建:
从一个可工作的应用(我们称其为目录“myOrigApp”,包名为com.foo.myapp)开始,只需将以下行添加到“default.properties”中即可将其转换为库:
android.library=true
现在以任何你喜欢的方式在一个兄弟目录中创建一个新应用程序(假设我们称之为目录“sibling”,包名为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",这是唯一的更改。
如果只运行构建脚本,则可能已经完成(我只需设置签名密钥)。
如果您想要设置像Idea这样的IDE,使库项目成为变体项目的依赖项,请按照以下步骤添加库项目到变体项目中(假设您已经为两个项目设置了项目):
<replaceregexp flags="g" byline="false">
<regexp pattern="old.package.string" />
<substitution expression="new.package.string" />
<fileset dir="src" includes="**/*.java" />
</replaceregexp>
Gradle
flavors,甚至是受到鼓励的。Gradle
,效果非常好。自定义应用程序图标、名称和字符串等。package
属性不必是代码所在的位置,只要您在清单文件的其他地方拼写出完全限定的类名即可(例如,activity android:name="com.commonsware.android.MyActivity"
而不是activity android:name=".MyActivity"
)。脚本化您的清单更改并使用Ant构建新的APK。据我所知,这应该可以工作。支持多个合作伙伴
准备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 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" preparefiles
创建build.xml 构建 $ ant debug 或者 $ ant release
我正在使用maven-android-plugin来实现这个目标。为生成源代码的目标指定一个AndroidManifest.xml文件,为最终apk目标指定另一个AndroidManifest.xml文件。这样,在生成R类和构建阶段期间,源代码项目将保留实际的源代码包名称,而适用于市场的清单包名称位于第二个AndroidManifest.xml文件中,该文件包含在最终的apk文件中。
我最终得到了一个修补源代码的脚本;虽然修补源代码听起来很冒险,但在版本控制的情况下,这种风险是可以接受的。
所以我先制作了一个版本,提交了源代码,再制作了另一个版本,提交了源代码,并查看差异,用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'))
好了,你明白了。现在你可以编写自己的脚本了。
我认为最好的方法是创建一个新项目并复制内容。步骤如下: - 创建一个没有类的新Android项目 - 创建包(包名称应与清单文件中对应),或者只需复制“gen”文件夹中的包名称 - 复制Java文件 - 复制可绘制文件夹 - 复制布局文件 - 复制您项目中使用的任何其他文件 - 复制清单文件的数据
对我来说,这个任务变得更简单了。