Building many similar Android .apk files at the same time? - android

I've got a single Android project which I need to build many .apk's from. The .apk's differ from each other in only a few ways:
there are a few values in the Android manifest file that differ from each other
some of the .apk's might exclude some files in the /res folder
different package name
What is the best way to build all these different .apk's automatically?
Thanks!

Use Android Maven plugin. It supports android library projects.
Create a multi-module project.
Put all common classes in one module (library).
Create a module for each app you want to distribute and make it depend on lib module.
Here is an example: https://github.com/jayway/maven-android-plugin-samples/tree/master/libraryprojects
(You will need to create several 'libraryprojects-mainapp' modules)

If you use git you can make different branches and checkout each one before building. You can also write a shell script to automate this if you are on a Mac or Linux.
Development can for example be done on an integration branch that will be merged into your three different branches.
I don't know if this makes it any easier for you, but it would be one way.

2011 is a long time ago.. but it seems that there is no Eclipse/Netbeans solution on this. I faced the same problem recently on a cordova project on Netbeans.
On bleeding edge Android Studio, there is a convenient solution called "flavours".
It's based on mirrored directories hierarchy. A "main" directory hierarchy that contains all files of a "main" build, and "flavours" directories hierarchies, each flavour directory contains files that will overwrite or complement those "main" ones at build time.
For my use (I can't migrate to Android Studio), I wrote a simple ant script to imitate that feature. It works on Netbeans and Eclipse, and I think it's some sort of project-independent.
For using it, the full project folder have to step back one level of hierarchy, and the original one needs to be placed on "main" directory. A build directory must be created, and multiple "flavors" folders be put inside "flavors", as this:
├── build.xml < - ant script file
├── main < - original project
├── flavors
│ ├── freeVersion < - files related with a freeVersion app
│ └── paidVersion < - files related with a paidVersion app
└── build < - temporary build folder
Running the script ($ ant change-flavor), it will ask witch flavor directory you want to build. After user input, it checks flavor directories existence, and copy all main directory into build folder, plus eventual "flavors" files, overwriting the "main" ones.
The resultant build folder is a complete new native Android/Cordova/whatever project, that can be normally compiled through the IDE.
<?xml version="1.0" encoding="UTF-8"?>
<project name="Flavors" basedir="." >
<property name="flavors.dir" value="flavors"/>
<property name="flavors.build.dir" value="build"/>
<property name="flavors.main.dir" value="main"/>
<target name="change-flavor">
<input message="Witch Flavor?" addproperty="flavor.dir" />
<fail message="Empty flavor not allowed">
<condition>
<equals arg1="${flavor.dir}" arg2=""/>
</condition>
</fail>
<fail message="Directory ${flavors.dir}/${flavor.dir} not exists">
<condition>
<not>
<available file="${flavors.dir}/${flavor.dir}" type="dir" />
</not>
</condition>
</fail>
<echo message="Deleting build dir ${flavors.build.dir}"/>
<delete includeemptydirs="true">
<fileset dir="${flavors.build.dir}" includes="**/*"/>
</delete>
<echo message="Copying from main ${flavors.build.dir}"/>
<copy todir="${flavors.build.dir}" includeemptydirs="true" >
<fileset dir="${flavors.main.dir}" includes="**"/>
</copy>
<echo message="Copying from flavor ${flavors.build.dir}"/>
<copy todir="${flavors.build.dir}" includeemptydirs="true" overwrite="true" >
<!-- exclude folder is here because my flavors directories are also netbeans
projects. If similar approach is used on eclipse, maybe put here .project
and .settings folders -->
<fileset dir="${flavors.dir}/${flavor.dir}" includes="**" excludes="nbproject/**"/>
</copy>
</target>
</project>
There is a penalty time for each flavor build, because there is no pre-compiled stuff, but in my case, I think its worth, since the only things that changes from a flavor to another are properties files and assets. Most of development process can be done on "main" project, flavors are just skins. Beside that, it avoids intromission on cordova own build system/netbeans integration.

Related

Ant Build for Android proj:: how to create the myproject-release.apk (signed) to a folder other than bin?

Ant Build for Android proj:: how to create the myproject-release.apk (signed) to a folder other than bin?
I have over a 100 projects and do not want all the release apks to go to the same folder.
But i cannot seem to find a way to do this. anyone who knows how to get this done ?
Add a custom Ant task that moves the APK file after your production build. You would put that Ant task towards the bottom of build.xml, just inside the trailing </project> close tag. For example, here is a jar task that creates a JAR file:
<target name="jar" depends="debug">
<jar
destfile="bin/CWAC-Layouts.jar"
basedir="bin/classes"
/>
</target>
In your case, you would create a task that uses <move>.
You will also want to modify the <!-- version-tag: 1 --> comment, just above where you are placing your Ant task, to be <!-- version-tag: custom -->. This will prevent Android's tools from clobbering your build.xml changes if they elect to modify build.xml themselves.
Then, run that Ant task.

Multiple automatic build ouputted to single directory

I'm new to automating builds using Ant. I have a bunch of Android project in Eclipse and I've gotten as far as setting up Ant builds I can run from the command line for each project, where a signed APK is generated in the 'bin' directory for whatever project I am running the build on.
If possible, I'd like to setup a single script to build each of my projects and output the signed APK's to one directory on my computer. Not exactly sure how to do that, whether I need to write a batch script or something else.
Any insight would be appreciated.
A batch script could be one option. Another, now that you're working with Ant, would be to create an Ant script that runs all the other Ant scripts and copies the results.
You can use the ant task to run one Ant script from another. You can then use the copy task to copy the resulting APK file to where you want it.
Here is an example that runs the default target of the build.xml file found in the directory path_to_other_project and then copies any APK files found in path_to_other_project/bin to destination_dir.
<ant dir="path_to_other_project"
antfile="build.xml"
inheritAll="false"
inheritRefs="false" />
<copy todir="destination_dir">
<fileset dir="path_to_other_project/bin" includes="*.apk" />
</copy>
If you have several projects, you can replace several similar calls to ant with one call to subant.

How do I reference external jar files in a common directory (not libs) to build android project using ant?

I would like to build several android projects that reference the same jar files using ant. I do not want to copy the jar file to each libs directory in the project (due to how the source control tree is set up). The answers I find here say "put them in the libs directory" which does not solve this problem.
So the question is, how do I configure my android ant build scripts to reference a common jar in a separate directory outside the project (not in "libs")?
In the sdk, there are ant files under tools/ant. In main_rules.xml, you can find the following code section:
<!-- Directory for the third party java libraries -->
<property name="jar.libs.dir" value="libs" />
<property name="jar.libs.absolute.dir" location="${jar.libs.dir}" />
<!-- create a path with all the jar files, from the main project and the
libraries -->
<path id="jar.libs.ref">
<fileset dir="${jar.libs.absolute.dir}" includes="*.jar" />
<path refid="project.libraries.jars" />
</path>
This is how the ant build code determines where to get the jars from. You could overload the value of jar.libs.dir in your build.properties since that is read before this section and would overload the value. Pointing that at a common directory you intend to use will do what you are suggesting. You may need to play with it some to get it to work the way you want.
One caveat is that I'm not sure if this will negatively impact other build life cycle activities.
It looks like the newer sdk tools make this easier to accomplish. I have a master libs folder that all my android projects share and I just added this to the ant.properties file to make it look up one level for those libs.
jar.libs.dir=../libs
As per the question here and my answer taken from here, the offered solution does not work as of now. Until the filed bug is fixed. The solution, as mentioned in the referenced answer, is copied below.
I add this to my custom_rules.xml template and it seems to be working fine.
I supposed it's only a few more lines than adding path to the ant.properties file until we get a better solution.
<target name="-pre-compile">
<path id="project.all.jars.path">
<path path="${toString:project.all.jars.path}"/>
<fileset dir="${jar.libs.dir}">
<include name="*.jar"/>
</fileset>
</path>
</target>
If you use Linux, just make symbolic links of the external jar files, then place them all in libs. Ant will work fine.
Edited
Make sure place all symbolic links directly in libs (not sub directories). I'm sure this can be re-config by ant script. But I'm not familiar with ant :-)

How to build an android app with external libraries using ant?

I have an existing project that builds fine using my IDE. I'd like to use the "android update" command to generate an ant buildfile for this project.
The buildfile is generated fine, but the build fails because it's not building with some jarfiles I have in my libs directory.
I'd like to figure out the proper way to tell ant to build with some external jar files in my libs directory. How should I do this? Is it a property in build.properties? Do I need to modify build.xml somehow? Or is there a different solution entirely?
but the build fails because it's not
building with some jarfiles I have in
my libs directory.
And your error message is...what? I suspect you may be misinterpreting the error message.
I'd like to figure out the proper way
to tell ant to build with some
external jar files in my libs
directory. How should I do this?
Just put them in libs/, as Ant will add everything in there to your build path. See this project, and this project, and this project for examples.
I spent some time trying to get the Facebook API to work with ant. The trick for me was to add this to my default.properties files.
android.library.reference.1=../Facebook
Where ../Facebook contains AndroidManifest.xml, etc. The real key being the relative path. Don't use an absolute path because Ant seems to treat your project directory as the root.
This should hold true for other library projects that you are including from source code.
I was dealing with similar issue. I'm building Android project on Jenkins using standard Ant build.xml (generated by Android SDK). I also have reference to another Java project with some shared domain classes. In Eclipse there is no problem with it. The domain project is a project reference. However on Jenkins this domain.jar is built by Maven and it was not accessible by Android project.
I have finally solved it by adding this at the end of build.xml:
<target name="-pre-build">
<copy todir="${jar.libs.dir}">
<fileset
dir="../path-to-another-project/target"
includes="*.jar" />
</copy>
</target>
This copies all jars from the target directory of another project into "libs" directory of my Android project. The -pre-build Ant target is automatically called before Android compilation starts.
I agree with Mark, however, if you're planning to modify your build script further - than you need to make it custom. Bring tasks from android/platforms/android-PLATFORMVERSION/templates/android_rules.xml to your build.xml and modify whatever you want to modify. Including location for external libs.

Android – multiple custom versions of the same app

Whats the best way to deploy several customized versions of a Android application?
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.
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)
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:
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:
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.
In that new, sibling directory edit "default.properties" to add the dependency:
android.library.reference.1=../myOrigApp
Copy over the Manifest from the original dir:
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.
If you just run the ant build scripts, you may be done. (I had to just set up signing keys.)
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):
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.)
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...
For example to replace it in all your java files under the src directory:
<replaceregexp flags="g" byline="false">
<regexp pattern="old.package.string" />
<substitution expression="new.package.string" />
<fileset dir="src" includes="**/*.java" />
</replaceregexp>
You definitely want to use Gradle flavors that comes natively, encouraged even, on Android Studio.
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.
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.
I probably didn't explain it the best, but that website does a pretty good job.
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.
Support Multiple Partners
Prepare 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="\1
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
Create build.xml
Build
$ ant debug
or
$ ant release
I'm using the maven-android-plugin to 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.
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.
I am not sure if it is the best solution. (And the code misses some 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"\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)
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).
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.
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
this has been simpler for me for the task

Categories

Resources