什么是部署Android应用程序的多个自定义版本的最佳方式?
目前我有一个脚本来交换资源文件夹以获取我的应用程序的自定义版本。它工作得很好,但所有自定义版本在AndroidManifest.xml中仍然具有相同的包名称。因此,无法同时安装两个自定义版本的应用程序。
This is one solution for this problem, but that has to be done by hand
您能想到一个更简单的解决方案,或者如何将其纳入一个特定的解决方案?
(顺便说一下:它不适用于色情/垃圾邮件/任何应用程序,甚至不是付费应用程序)
答案 0 :(得分:23)
也许内置的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 .
编辑复制的Manifest文件,将其包名更改为新变种“com.foo.myVarient”;这是唯一的变化。
如果您只是运行ant构建脚本,那么您可能已经完成了。 (我不得不设置签名密钥。)
如果要设置类似于IDE的IDE以将库项目作为变体项目的依赖项,请按照以下步骤将库项目添加到变体项目(假设您已经为两者设置了项目) :
答案 1 :(得分:6)
我对类似的事情所做的就是使用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>
答案 2 :(得分:6)
肯定想要在Android Studio上使用Gradle
flavors,甚至鼓励使用{{3}}。
它似乎很好地解释了所有基础知识。我今天刚刚完成转换为Gradle
,效果很好。自定义应用程序图标,名称和字符串等。
正如网站所解释的那样,此设计背后的部分目的是使其更具动态性,并且更容易使用基本相同的代码创建多个APK,这听起来与您正在做的类似。
我可能没有解释它最好,但该网站做得非常好。
答案 3 :(得分:4)
链接解决方案不必手动完成。请记住,package
元素中的<manifest>
属性不必是代码所在的位置,只要您在清单中的其他位置拼出完全限定的类(例如{{1}而不是activity android:name="com.commonsware.android.MyActivity"
)。编写清单更改脚本并使用Ant构建新的APK。 AFAIK,应该有用。
答案 4 :(得分:2)
支持多个合作伙伴 准备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
答案 5 :(得分:1)
我using the maven-android-plugin来实现这个目标。为generate-sources目标指定一个AndroidManifest.xml,为最终apk目标指定另一个AndroidManifest.xml。这样,源代码项目在生成R类和构建阶段期间保留实际的源代码包名称,而市场调整的清单包名称位于第二个AndroidManifest.xml中,该文件包含在最终的apk文件中。
答案 6 :(得分:0)
我最后编写了一个补充源代码的脚本;修补源声音有风险,但在版本控制存在的情况下风险是可以接受的。
所以我创建了一个版本,提交了源代码,创建了另一个版本,提交了源代码,然后查看diffs在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'))
嗯,你明白了。现在你可以编写自己的脚本了。
答案 7 :(得分:0)
我认为最好的方法是创建一个新项目并复制这些东西。脚步, - 创建没有类的新android项目 - 创建包(包名称应该与清单文件中的包名相对应),或者只是复制'gen'文件夹中的包名称 - 复制java文件 - 复制drawable文件夹 - 复制布局文件 - 复制你的项目中使用的任何其他文件 - 复制清单文件的数据
这对我来说更简单