Android – 同一个应用程序的多个自定义版本

什么是部署一个Android应用程序的几个定制版本的最佳方式?

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

这是解决这个问题的方法之一,但这必须由人手完成

你能想到一个更简单的解决scheme,或者如何将这个解决scheme构build成一个匆匆而过的?

(顺便说一句:这是不是一个色情/垃圾邮件/任何应用程序,甚至不支付一个)

也许内置的Android“库”概念在原始post的时候还没有完全烘托,但它可能是2011年的首选方法。请按照以下步骤进行构build:

从一个正在运行的应用程序(我们称之为“myOrigApp”,包com.foo.myapp)开始,只需将此行添加到“default.properties”中即可创build一个库:

 android.library=true 

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

在新的兄弟目录中编辑“default.properties”来添加依赖关系:

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

复制原始目录中的清单:

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

编辑复制的Manifest文件,将其包名更改为新的变体“com.foo.myVarient”; 这是唯一的改变。

如果你只是运行ant构build脚本,你可能会完成。 (我不得不设置签名密钥。)

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

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

我所做的类似这样的事情就是使用antlib任务,然后遍历所有的java和xml文件,将旧的包stringreplace为新的包string。 根据软件包,这些文件是否不在正确的srcpath中并不重要。 只是做一个正则expression式replace所有的文件就足以让我得到这个工作…

例如,要将其replace为src目录下的所有java文件:

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

一定要使用Gradle风格 ,即使在Android Studio上也是如此。

这似乎很好地解释了所有的基础知识。 我刚刚完成了对Gradle转换,效果很好。 自定义应用图标,名称和string等

正如网站所解释的,这个devise背后的目的之一就是让它更具dynamic性,更容易让多个APK使用基本相同的代码来创build,这听起来与您所做的相似。

我可能没有解释最好的,但该网站做得不错。

链接解决scheme不一定要手工完成。 请记住, <manifest>元素中的package属性不一定要放在代码所在的位置,只要在清单中的其他地方拼出完全限定的类(例如activity android:name="com.commonsware.android.MyActivity"而不是activity android:name=".MyActivity" )。 脚本清单更改,并使用Ant来构build一个新的APK。 AFAIK,这应该工作。

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

为不同的合作伙伴build立项目

 <!--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”preparefiles

创buildbuild.xml生成$ antdebugging或$ ant版本

我正在使用maven-android-plugin来实现这一点。 为生成源目标指定一个AndroidManifest.xml,为最终apk目标指定另一个AndroidManifest.xml。 这样,源代码项目在生成R类和构build阶段时会保留实际的源代码包名称,而市场适应的清单包名称位于包含在最终apk文件中的第二个AndroidManifest.xml中。

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

所以我做了一个版本,提交了源代码,创build了另一个版本,提交了源代码,并且看差异在Python中写了一个修补脚本。

我不确定这是否是最好的解决scheme。 (而代码会遗漏一些os.path.joins)

脚本的核心是以下function:

 # 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-cfgnameassets (事实上​​,对于我来说,在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')) 

反正你懂这个意思。 现在你可以写你自己的脚本。

我认为最好的办法是创build一个新项目并复制这些东西。 步骤, – 没有类创build新的android项目 – 创build软件包(软件包名称应该对应于清单文件中的软件包名称),或者只复制包名称在“gen”文件夹中 – 复制java文件 – 复制可绘制的文件夹 – 复制布局文件 – 复制在你的项目中使用的任何其他文件 – 复制清单文件的数据

这对于我来说更简单了