There are good reasons to want to build two versions of the same Android application. The most common scenario is to produce a free demo/reduced-functionality version of a non-free app. You could achieve this by maintaining two separate source trees but the duplication would make most developers wince. So the question is how to build two differing apps without duplicating the code and resources?
Building Android Apps with Custom Ant Scripts
The answer is Ant. Maybe it’s also possible with Eclipse. I doubt it but I don’t know and I don’t care to find out. As I mentioned previously, the Android Ant build system is fairly painless to use. If you want to customise it though you’ll have to do a fair bit of digging as it doesn’t appear to be documented anywhere. The first place to look is the <sdk_dir>/platforms/<target_dir>/templates/android_rules.xml
file that is used by the build.xml
that the Android tools generate for you. This will give you a good idea of the various stages of the build but if you want to experiment with the Android Ant tasks you’ll have to look at the source to see what attributes they support (making sure that the version you are looking at is the same as the version you are using). The AaptExecLoopTask
is just a simple wrapper around the aapt
tool but the ApkBuilderTask
is non-trivial.
The outline of a minimal Ant build for an Android app looks something like this:
- Generate the
R.java
file from the resources using the theexec
task and theaapt
tool with the-J
option. - Compile all of the app source, including the class generated in step 1.
- Use the
dx
tool to convert the Java bytecode into a .dex (Dalvik executable) file. - Use the
aapt
tool (either via theexec
task or via the providedAaptExecLoopTask
) to package the resources. - Build the .apk package using the
ApkBuilderTask
. - If you are building a release package rather than a debug package, sign the .apk file using the
signjar
task. - Use
exec
to run thezipalign
tool on the .apk file.
More advanced applications will involve additional steps, such as running the aidl
tool.
Once you have the outline of your build in place, you can start to customise it using your standard issue Ant prowess.
Different Resources for Different Versions
In my scenario I don’t mind shipping all of the code with both versions of the application. The difference is just the initial Activity that each uses. It doesn’t add much bloat to the package and the code is useless without the full set of resources so there is no danger of somebody hacking the demo version into a fully functioning version. In other words, I don’t need to worry about excluding certain classes from one version or the other.
I do however only want to include a subset of the resources in the demo version. The way I have achieved this is to replace the default res
directory with three directories: res-common
, res-demo
and res-full
. I then use the Ant copy task to construct the res
directory from the appropriate pair of directories prior to building.
Different Manifests
Because I want to invoke different activities, I need to define the AndroidManifest.xml
differently for each version. My first idea was to just have two different files with different names but I discovered that the Android tools get upset if the file is not called AndroidManifest.xml
, even if you explicitly specify which file to use. This just means that I have to copy the chosen manifest so that it has the correct name.
The biggest problem with building two versions from the same tree is resolving conflicts in the application package name. Each Android application must have a unique application package. This is specified in the manifest. You can just use the same package name for both versions but users will run into problems if they ever try to install both versions at the same time. They will likely end up not being able to use either. Despite the drawbacks, this method seems to be widely used judging by the number of apps in the Android Market that warn users to uninstall the demo version first.
So we’ll just change the package name in one of the manifests then? Yeah, that would be nice. The problem is that the package name specified in the manifest is the package into which the R
class is generated. If the two versions of your application have their R
classes in different packages then common classes will not compile for both versions.
Android 2.1 introduces the --custom-package
option for the aapt
tool which allows you to over-ride where R.java
will be generated. So the idea is to set the application package differently in one of the manifests but then to use this option to make sure that R.java
is still in the same place as in the other version. I tried this and it resolved my compile problems but there were resource problems at runtime. I didn’t fully investigate what was wrong because I found another approach that appears to work.
The aapt
tool also has the --rename-manifest-package
option. Leaving the application package the same in both manifests and then using this option at step 4 above, I was able to generate a working demo version and full version from the same source tree and have them both functional when installed at the same time.
The whole custom Ant build exercise was not nearly as straightforward as I would have liked, mostly due to lack of documentation, but it is at least a working solution to the problem. Another, probably more recommended, way of achieving something similar would be to use library projects.