So you're building an Android app, and you want your app to display its build number on an About screen. You want the build number to be a unique identifier like the Subversion revision number. That's easy, you say: I'll build a simple About screen, put a TextView in it, and use Subversion keyword substitution, like this:
package com.kasperowski.example;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class BuildNumberExample extends Activity {
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View aboutView = aboutWithKeywordSubstitution();
setContentView(aboutView);
}
View aboutWithKeywordSubstitution() {
TextView aboutView = new TextView(this);
aboutView.setText("Build number: $Rev$");
return aboutView;
}
}
And for bonus points, you could play some regex tricks or something to make the version number look like 42 instead of $Rev: 42$.
But, uh-oh! That's the revision number for that file, not the global revision number for your whole project. So it's no good as a build number.
You could play other games, like using a serial number file and writing a script that increments the serial number every time you build. But then you'll have a different problem: my sandbox and your sandbox will increment the serial number independently, oftentimes using colliding serial numbers. It would be a sandbox-specific serial number, hardly a good way to identify a build. You could build a shared database column with a globally incrementing serial number, but that's overkill.
Instead, use
svnversion, a command line tool that returns the global version number of your repository. It is a stable, unique number that everyone can use, and it yields a true build identifier. At the command line, type svnversion, and you'll see your repository version number:
$ svnversion
22
To inject the svnversion number into your Android app's About screen, add a new resource to your
strings.xml file:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, BuildNumberExample!</string>
<string name="app_name">Build Number Example</string>
<string name="app_svnversion">foo</string>
</resources>
Then, make sure you have an ant build script. I created my project using Eclipse, so I don't have a build script yet. Tell Android to create an ant build script for you:
$ android update project --path .
Now you have a
build.xml file, and you can type things like
ant clean,
ant debug, and
ant install to build and install your app. Test your build script to make sure it works:
$ ant clean
Buildfile: C:\usr\local\workspace\build-number-example\build.xml
[setup] Project Target: Android 1.6
[setup] API level: 4
clean:
[delete] Deleting directory C:\usr\local\workspace\build-number-example\bin
[delete] Deleting directory C:\usr\local\workspace\build-number-example\gen
BUILD SUCCESSFUL
Total time: 46 seconds
$ ant install
Buildfile: C:\usr\local\workspace\build-number-example\build.xml
[setup] Project Target: Android 1.6
[setup] API level: 4
-compile-tested-if-test:
-dirs:
[echo] Creating output directories if needed...
[mkdir] Created dir: C:\usr\local\workspace\build-number-example\gen
[mkdir] Created dir: C:\usr\local\workspace\build-number-example\bin
[mkdir] Created dir: C:\usr\local\workspace\build-number-example\bin\classes
-resource-src:
[echo] Generating R.java / Manifest.java from the resources...
-aidl:
[echo] Compiling aidl files into Java classes...
compile:
[javac] C:\android-sdk-windows\platforms\android-1.6\templates\android_rules.xml:248: warning: 'includeantruntime' was not set, defaulting to build.sysclasspath=last; set to false for repeatable builds
[javac] Compiling 2 source files to C:\usr\local\workspace\build-number-example\bin\classes
-dex:
[echo] Converting compiled files and external libraries into C:\usr\local\workspace\build-number-example\bin\classes.dex...
[echo]
-package-resources:
[echo] Packaging resources
[aaptexec] Creating full resource package...
-package-debug-sign:
[apkbuilder] Creating BuildNumberExample-debug-unaligned.apk and signing it with a debug key...
[apkbuilder] Using keystore: C:\Documents and Settings\kasper\.android\debug.keystore
debug:
[echo] Running zip align on final apk...
[echo] Debug Package: C:\usr\local\workspace\build-number-example\bin\BuildNumberExample-debug.apk
install:
[echo] Installing C:\usr\local\workspace\build-number-example\bin\BuildNumberExample-debug.apk onto default emulator or device...
[exec] pkg: /data/local/tmp/BuildNumberExample-debug.apk
[exec] Success
[exec] 17 KB/s (13601 bytes in 0.781s)
BUILD SUCCESSFUL
Total time: 2 minutes 2 seconds
$
Add a new target to your
build.xml file. This target executes svnversion and injects it into your
strings.xml file.
<target name="foo-update-svnversion">
<exec outputproperty="build.svnversion" executable="svnversion">
<arg line="-n -c" />
</exec>
<property name="match.start" value="<string name="app_svnversion">"/>
<property name="match.end" value="</string>"/>
<replaceregexp file="res/values/strings.xml"
match="${match.start}.*${match.end}"
replace="${match.start}${build.svnversion}${match.end}">
</replaceregexp>
</target>
At the command line, invoke the new ant target and then look at your
strings.xml file. You'll see the svnversion number in
strings.xml:
$ ant foo-update-svnversion
Buildfile: C:\usr\local\workspace\build-number-example\build.xml
[setup] Project Target: Android 1.6
[setup] API level: 4
foo-update-svnversion:
BUILD SUCCESSFUL
Total time: 2 seconds
$ cat res/values/strings.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="hello">Hello World, BuildNumberExample!</string>
<string name="app_name">Build Number Example</string>
<string name="app_svnversion">22</string>
</resources>
$
Now add some build targets to integrate your svnversion target with the default Android targets:
<target name="foo-debug" depends="foo-update-svnversion">
<antcall target="debug"/>
</target>
<target name="foo-install" depends="foo-update-svnversion">
<antcall target="install"/>
</target>
Finally, let's go back to your About screen. Add a method,
aboutWithSvnVersion(), that grabs the svnversion resource and displays it on the screen:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View aboutView = aboutWithSvnversion();
setContentView(aboutView);
}
View aboutWithSvnversion() {
TextView aboutView = new TextView(this);
String svnversion = getResources().getString(R.string.app_svnversion);
aboutView.setText("Build number: " + svnversion);
return aboutView;
}
Build your Android app by typing
ant foo-debug or
ant foo-install. This does exactly what you want, and now the world is safe again.
Resources: