Android – 同一应用程序的多个自定义版本
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1222302/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Android – multiple custom versions of the same app
提问by Ulrich Scheller
Whats the best way to deploy several customized versions of a Android application?
部署多个自定义版本的 Android 应用程序的最佳方法是什么?
Currently I have a script to exchange the resource folder for getting a customized version of my app. It works great, but all custom versions still have the same package name in the AndroidManifest.xml. Therefore it is not possible to install two customized versions of the app at the same time.
目前我有一个脚本来交换资源文件夹以获得我的应用程序的自定义版本。它运行良好,但所有自定义版本在 AndroidManifest.xml 中仍然具有相同的包名称。因此,不可能同时安装两个自定义版本的应用程序。
This is one solution for this problem, but that has to be done by hand
Can you think of a more easy solution, or how this could be built into a skript?
你能想到一个更简单的解决方案,或者如何将它内置到脚本中?
(btw: it is not for a porn/spam/whatever app, not even a paid one)
(顺便说一句:它不适用于/垃圾邮件/任何应用程序,甚至不是付费应用程序)
采纳答案by larham1
Perhaps the built-in Android "library" concept was not fully baked at the time of the original post, but it may be the preferred method as of 2011. Follow these steps for an ant build:
也许在最初发布时,内置的 Android“库”概念还没有完全成熟,但它可能是 2011 年的首选方法。请按照以下步骤构建 ant:
Starting from a working app (let's call it directory "myOrigApp", package com.foo.myapp), just add this line to "default.properties" to make it a library:
从一个工作应用程序开始(我们称之为目录“myOrigApp”,包 com.foo.myapp),只需将此行添加到“default.properties”以使其成为一个库:
android.library=true
Now create a new app in a sibling directory in any way you prefer (let's call it directory "sibling", package com.foo.myVariant). Using Intellij Idea, for example, create a project 'from scratch' with directory 'sibling' and it will create all the files/directories you would normally need.
现在以您喜欢的任何方式在同级目录中创建一个新应用程序(我们称之为目录“同级”,包 com.foo.myVariant)。例如,使用 Intellij Idea,使用目录“sibling”“从头开始”创建一个项目,它将创建您通常需要的所有文件/目录。
In that new, sibling directory edit "default.properties" to add the dependency:
在那个新的同级目录中编辑“default.properties”以添加依赖项:
android.library.reference.1=../myOrigApp
Copy over the Manifest from the original dir:
从原始目录复制 Manifest:
cd sibling
cp ../myOrigApp/AndroidManifest.xml ../myOrigApp/local.properties ../myOrigApp/build.properties .
Edit that copied Manifest file to change its package name to your new variant, "com.foo.myVarient"; that's the only change.
编辑复制的 Manifest 文件以将其包名称更改为您的新变体“com.foo.myVarient”;这是唯一的变化。
If you just run the ant build scripts, you may be done. (I had to just set up signing keys.)
如果你只是运行 ant 构建脚本,你可能就完成了。(我只需要设置签名密钥。)
If you want to set up an IDE like Idea to have the library project as a dependent of the variant project, follow these steps to add a library project to the variant project (assumes you already have a project set up for both):
如果您想设置 IDE 之类的 IDE 以将库项目作为变体项目的依赖项,请按照以下步骤将库项目添加到变体项目(假设您已经为两者设置了一个项目):
- Open the original project, bring up Project Settings, select your Facet and check "Is Library Project" and save.
- Open the variant project, bring up Project Settings, select Modules
- Add a module
- Select “Import existing module”
- Browse to the Original directory (myOrigApp) and select its .iml file (IntelliJ project source file)
- Click "Finish." (The library project is added as a module within the variant project.)
- In the modules list click over the Variant project to select it.
- On the right hand side select the "Dependencies" tab.
- Click "Add…"
- Choose "Module dependency…" (A list should appear that includes the name of the module/library you previously added to the project--perhaps the only entry in the list).
- Select the library project you added and press OK. (It will be added to the list of dependencies of your project.)
- Press OK to finish configuring the project. (You should see 2 modules, with the library's resources and classes available and recognized in the Variant project.)
- 打开原始项目,调出项目设置,选择您的构面并选中“是库项目”并保存。
- 打开变体项目,调出项目设置,选择模块
- 添加模块
- 选择“导入现有模块”
- 浏览到原始目录 (myOrigApp) 并选择其 .iml 文件(IntelliJ 项目源文件)
- 单击“完成”。(库项目作为模块添加到变体项目中。)
- 在模块列表中单击 Variant 项目以选择它。
- 在右侧选择“依赖项”选项卡。
- 点击“添加...”
- 选择“模块依赖...”(应该会出现一个列表,其中包含您之前添加到项目中的模块/库的名称——可能是列表中唯一的条目)。
- 选择您添加的库项目,然后按 OK。(它将被添加到您项目的依赖项列表中。)
- 按 OK 完成项目配置。(您应该看到 2 个模块,以及在 Variant 项目中可用和识别的库资源和类。)
回答by craned
You definitelywant to use Gradle
flavorsthat comes natively, encouraged even, on Android Studio.
您肯定希望在 Android Studio 上使用原生的甚至鼓励的Gradle
风格。
It seems to explain all the basics really well. I just finished converting to Gradle
today, and it works great. Custom app icons, names, and strings, etc.
它似乎很好地解释了所有基础知识。我刚刚完成转换到Gradle
今天,效果很好。自定义应用程序图标、名称和字符串等。
As the website explains, part of the purpose behind this design was to make it more dynamic and more easily allow multiple APKs to be created with essentially the same code, which sounds similar what you're doing.
正如该网站所解释的那样,这种设计背后的部分目的是使其更具动态性,并且更容易允许使用基本相同的代码创建多个 APK,这听起来与您正在做的事情相似。
I probably didn't explain it the best, but that website does a pretty good job.
我可能没有最好地解释它,但该网站做得很好。
回答by Prashast
What I did for something similar to this is to just use an antlib task and then go through all java and xml files to replace my old package string to the new package string. It didn't matter if the files were not in the correct src paths according to the package. Just doing a regex replace for all the files was enough for me to get this working...
我为类似的事情所做的只是使用一个 antlib 任务,然后遍历所有 java 和 xml 文件以将我的旧包字符串替换为新包字符串。根据包,文件是否不在正确的 src 路径中并不重要。只需对所有文件进行正则表达式替换就足以让我工作......
For example to replace it in all your java files under the src directory:
例如,在 src 目录下的所有 java 文件中替换它:
<replaceregexp flags="g" byline="false">
<regexp pattern="old.package.string" />
<substitution expression="new.package.string" />
<fileset dir="src" includes="**/*.java" />
</replaceregexp>
回答by CommonsWare
The linked-to solution does not have to be done by hand. Bear in mind that the package
attribute in the <manifest>
element does not have to be where the code resides, so long as you spell out the fully-qualified classes elsewhere in the manifest (e.g., activity android:name="com.commonsware.android.MyActivity"
rather than activity android:name=".MyActivity"
). Script your manifest change and use Ant to build a new APK. AFAIK, that should work.
链接到的解决方案不必手动完成。请记住,元素中的package
属性<manifest>
不必是代码所在的位置,只要您在清单的其他地方拼出完全限定的类(例如,activity android:name="com.commonsware.android.MyActivity"
而不是activity android:name=".MyActivity"
)。编写清单更改脚本并使用 Ant 构建新的 APK。AFAIK,那应该有效。
回答by figofuture
Support Multiple Partners Prepare config.xml
支持多个合作伙伴准备 config.xml
Build project for different partner
为不同的合作伙伴构建项目
<!--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=" import com.${pkg.name}.R;" />
<fileset dir="${src}" includes="**/*.java" />
</replaceregexp>
</target>
Prepare Files $ ant -f config.xml -Dpartner.dir="xxx" -Dpkg.name="xxx" -Dver.code="xxx" -Dver.name="xxx" preparefiles
准备文件 $ ant -f config.xml -Dpartner.dir="xxx" -Dpkg.name="xxx" -Dver.code="xxx" -Dver.name="xxx" preparefiles
Create build.xml Build $ ant debug or $ ant release
创建 build.xml Build $ant debug 或 $ant release
回答by Fredrik Jonson
I'm using the maven-android-pluginto achieve this. Specify one AndroidManifest.xml for the generated-sources goal and another AndroidManifest.xml for the final apk goal. That way the source code project retains the actual source code package name during generation of the R class and the build phase, while the market adapted manifest package name is in the second AndroidManifest.xml which is included in the final apk file.
我正在使用 maven-android-plugin来实现这一点。为生成源目标指定一个 AndroidManifest.xml,为最终 apk 目标指定另一个 AndroidManifest.xml。这样,源代码项目在 R 类的生成和构建阶段保留了实际的源代码包名称,而市场适应的清单包名称在第二个 AndroidManifest.xml 中,它包含在最终的 apk 文件中。
回答by 18446744073709551615
I wound up with a script that patches the sources; patching the source sounds risky, but in presence of version control the risk is acceptable.
我最终编写了一个修补源代码的脚本;修补源听起来有风险,但在存在版本控制的情况下,风险是可以接受的。
So I made one version, committed the source, made the other version, committed the source, and looking at diffs wrote a patching script in Python.
所以我制作了一个版本,提交了源代码,制作了另一个版本,提交了源代码,然后查看差异,用 Python 编写了一个补丁脚本。
I am not sure if it is the best solution. (And the code misses some os.path.joins)
我不确定这是否是最好的解决方案。(并且代码遗漏了一些 os.path.joins)
The heart of the script is the following 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""+newtext+r""
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)
And you may find the following one of use:
您可能会发现以下用途之一:
# 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)
There is also a function that copies assets from x-assets-cfgname
to assets
(earlier it turned out that for me it is more convenient to have a subdirectory in assets
).
还有一个从x-assets-cfgname
to复制资产的功能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'))
Well, you get the idea. Now you can write your own script.
反正你懂这个意思。现在您可以编写自己的脚本了。
回答by M. Usman Khan
I think the best way is to create a new project and copy the stuff. steps, - create new android project without a class - create package (package name should be corresponding to the one in the manifest file), or just copy the package name in the 'gen' folder - copy the java files - copy the drawable folders - copy the layout files - copy any other file(s) used in ur project - copy manifest file's data
我认为最好的方法是创建一个新项目并复制这些东西。步骤,-创建没有类的新android项目-创建包(包名称应与清单文件中的包名称相对应),或者只需复制'gen'文件夹中的包名称-复制java文件-复制可绘制文件夹- 复制布局文件 - 复制您项目中使用的任何其他文件 - 复制清单文件的数据
this has been simpler for me for the task
这对我来说更简单的任务