This is an old post. Some dead links have been replaced and may link to outdated, wrong or no longer active content.
Rationale
As part of my master’s thesis, I’m comparing and evaluating several cross-platform mobile frameworks. I also wanted to have PhoneGap (now called Cordova) in the comparison, but since it does not include a UI library, I decided to combine it with Sencha Touch 2 for that purpose. Why not jQuery Mobile you may ask? Rhodes includes jQuery Mobile, and I covered Rhodes already in my comparison – and I don’t want to compare too similar frameworks. Also, Sencha Touch only includes dependencies that are actually used (concatenated into a single file), thus it seemed worthwhile to check its performance. And yes, Sencha Touch comes with native packaging for Android, but 1) that didn’t work for me, 2) does not include the device functionality offered by PhoneGap and 3) should be as performant as the PhoneGap app wrapper since it uses the platform’s web view component.
Update: Sencha Touch 2.1 and Sencha Command Utility
Somebody commented that people are having trouble when using the newer Sencha Touch 2.1 SDK. This article walks you through setting up version 2.0. Since I don’t use Sencha Touch for myself and the Sencha team might change their mind again to reinvent their own build toolchain, I will not update the whole article. However, I found that there are only few differences introduced by the new “Sencha Command utility” (replacing “SDK tools”) and build system, and my example application below works fine with Sencha Touch 2.1 if you consider the following differences:
-
sencha app createis nowsencha generate app -
sencha app build testingwill not consider the argument-d android/assets/wwwanymore but looks into thebuild.xmlfile to find the build output directory. It defaults to<project dir>/build/<project name>/testing. -
Unchanged: The
sencha app buildprocess still happily returns 0 in case of errors, so the wrapper script is still necessary. -
You will have to adapt the wrapper script or your
build.xmlfile to output the application into theandroid/assets/www folder.
Let’s get going
First of all, I’m using Eclipse to package the Android app, so make sure you have everything
installed to create a simple Android project. In this article, I’m
using Windows but it should work the same way on Linux and others. You will also need the
Sencha Touch SDK and tools –
ensure that the sencha command works and that it is
always on the PATH (on Windows, just restart your computer). At the time of writing, the Sencha Touch SDK version was
2.0.1.1 and the Sencha Touch SDK tools version was 2.0.0-beta3. For some black magic build automation, you will also
need Python (UPDATE: I use 2.7, but it should also work with 3.x).
The directory structure
Since the Android project will later go in a subdirectory, let me first explain the directory structure that our application will have:
-
AndroidSencha
-
android
-
assets
-
www
-
-
libs
-
res
-
src
-
.project (and other Eclipse Android project files)
-
-
app
-
resources
-
sdk
-
.senchasdk
-
app.js
-
app.json
-
cordova-x.y.z.js
-
index.html
-
sencha_wrapper.py
-
The AndroidSencha directory contains the app scaffolding created by Sencha Touch, i.e. app (models, stores, views,
controllers), resources (CSS, images), sdk (necessary Sencha Touch SDK files), .senchasdk (points to the SDK),
app.js, app.json, cordova-x.y.z.js and index.html. The android folder will be created manually and contains
our Eclipse project. And sencha_wrapper.py is my wrapper script for the sencha command that will be explained later.
If this does not make sense to you, check out the finished application at Github or just bear with me in the rest of the article.
Create the Sencha app
cd "/path/to/downloaded/sencha/sdk"
sencha app create AndroidSencha "/path/where/you/want/the/app/AndroidSencha"
The command should now copy/create some files and directories.
Create the Android project in a subdirectory
Now it’s time to do some bootstrapping for the Android part. In the newly created application
directory (the one that contains app.js), create a folder named android and create a new
Android project there using Eclipse:
Note: I use Android 2.3.3 (SDK version 10) as build target and the package name org.dyndns.andidogs.androidsencha.
You might run into the error "Can’t find variable: Ext" or similar if you use the 2.2 or older emulator (see
here).
Set up PhoneGap
PhoneGap is set up almost as usual with Android projects, following the
official guide,
but some steps differ a bit. In the android folder:
-
Create the folders
/libsand/assets/www -
Copy
cordova-x.y.z.jarto/libsand add it to the build path using Eclipse -
Copy the
xmlfolder from PhoneGap to/res -
Make the changes to the main activity (in my case
AndroidSenchaActivity) -
Different and optional: Add
setIntegerProperty("loadUrlTimeoutValue", 60000);before thesuper.loadUrlcall in case you run into timeout problems! -
Make the changes to
AndroidManifest.xml -
Different: It is not necessary to put
cordova-x.y.z.jsand the sampleindex.htmlfile into/assets/www, but you might want to do that and run the app on the emulator to see if the PhoneGap "Hello World" works!! Note that theindex.htmlwill later be overwritten automatically, so don’t change it in the/assets/wwwdirectory, in fact don’t change anything there! (you will see later why)
Test if Sencha Touch is working
We have a main app directory for the Sencha Touch application and a subdirectory for Android. You should now check if the Sencha Touch application actually works. By default, it should contain a tab bar with two different views. Fire up a web server in that main directory and open it up in a browser (should be a Webkit browser, not Firefox). With Python, it’s as simple as:
python -m SimpleHTTPServer 8000
# Multithreaded alternative if you have Twisted installed:
twistd.py web --path . --port 8000
Note that it might take some time to load (especially with the single threaded SimpleHTTPServer of Python). It should look something like this:
Important: The fact that it works in the browser does not mean it works on a mobile device/emulator. It took me a
while to find out that you have to change "logger": "no" in app.json to "logger": "false". Else you will get an
error like "Error: [Ext.Loader] Failed loading 'file:///android_asset/www/sdk/src/log/Logger.js', please verify that
the file exists at file:///android_asset/www/sdk/sencha-touch.js:7908".
PhoneGap working? Sencha Touch working? Time to combine them!
Including PhoneGap
First of all, add the PhoneGap script as dependency, so copy cordova-x.y.z.js to the main folder and change app.json
to include it – therefore you only have to add it to the key "js".
"js": [
{
"path": "cordova-1.8.0rc1.js"
},
Building
Sencha Touch comes with the sencha command line utility that can build an application, i.e. scan its dependencies, concatenate necessary files into a final app.js, copy resources etc. What we want to accomplish is to put that build output into the Android app’s /assets/www folder. And that’s why I said, don’t edit any files there because they will get overwritten.
I am using a simple builder configuration in Eclipse to run this command (will be explained below). Unfortunately,
it seems that Eclipse does not stop the build if the sencha command returns an error – the sencha command actually
always returns 0, but even if I wrap it with a script, Eclipse does not stop the build on non-zero return codes. Also,
when set up as builder, Eclipse hides the sencha command output after completion (don’t know why?). That is a problem,
because if you have a syntax error or other mistake in your Sencha Touch app, then you will see only the loading
indicator and some unhelpful "[Ext.Loader] Failed loading…" error in LogCat once you try and start the app. I work
around this problem with a dirty hack wrapper for the sencha command. In the main folder (the one with app.js),
add the file sencha_wrapper.py with the following content:
import os
import subprocess
import sys
def contains_errors(s):
return '[ERROR]' in s
def get_errors(s):
ret = ''
for line in s.splitlines():
if contains_errors(line):
ret += line + '\n'
return ret
print('Running Sencha command...')
try:
proc = subprocess.Popen(['sencha.bat' if os.name == 'nt' else 'sencha'] + list(sys.argv[1:]),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
stdout, stderr = proc.communicate()
# Try to decode output to Unicode
stdout = stdout.decode('utf-8', 'replace')
stderr = stderr.decode('utf-8', 'replace')
if proc.returncode != 0 or contains_errors(stdout) or contains_errors(stderr):
return_code = proc.returncode or 1
sys.stderr.write('Command failed\n')
else:
return_code = 0
sys.stdout.write(stdout)
sys.stderr.write(stderr)
except Exception as e:
stdout = ''
stderr = ('[ERROR] Failed to execute sencha command, did you reboot and ensure the sencha command is always on the '
'PATH? (%s)' % str(e))
return_code = 2
# Eclipse does not seem to stop the build even for return codes != 1, so let's be a bit more cruel
with open(os.path.join('android', 'AndroidManifest.xml'), 'r+t') as f:
MAGIC = ('SENCHA BUILD FAILED, PLEASE CHECK FOR ERRORS AND RE-RUN BUILD (THIS LINE IS REMOVED AUTOMATICALLY IF '
'SENCHA BUILD SUCCEEDS)')
content = f.read()
magicPosition = content.find(MAGIC)
if magicPosition != -1:
content = content[:magicPosition].strip()
if return_code != 0:
content += '\n' + MAGIC + '\n' + get_errors(stdout + '\n' + stderr)
f.seek(0)
f.write(content)
f.truncate()
exit(return_code)
This script runs the sencha command with the passed arguments and checks if the string "[ERROR]" occurs in the output
(by the way, they also don’t use stderr as they should) and if so, writes these errors to the end of
AndroidManifest.xml and thus stops the Android build process because that XML file is no longer valid. As I said,
a dirty hack. These lines are automatically removed once you correct the Sencha Touch app mistakes and run the script
again.
So let’s try that out. From the main folder, run python sencha_wrapper.py app build testing -d android/assets/www.
If successful, it will show no errors and end with "Embedded microloader into index.html". Now go to Eclipse, refresh
the project (select project name and hit F5) and then run it. The app should work without problems (only the emulator
is slow as hell):
Build automation
Great progress! But of course you don’t want to use the command line and refresh manually every time you want to run your app, so let’s automate this. In Eclipse, right click the project, select "Properties" and then "Builders", "New…" and "Program". Configure it as follows (your Python path will vary):
Click "OK" and use the "Up" button to move that builder to the beginning of the list:
Ensure that AndroidManifest.xml is refreshed after the script is run:
As a bonus, you can set up another builder called "Force recompile" that ensures that every time you click the run
button in Eclipse, the Sencha Touch app is recompiled and Eclipse rebuilds the Android app instead of bringing the
current intent to the foreground as it would normally do if it doesn’t recognize any changes (which it can’t because
Sencha Touch app changes are in the parent directory!). The builder is configured as follows, note that you will need
touch.exe (equivalent for the touch command of Linux, for Windows you can use the one from
msysgit or the
gnuwin32 coreutils package):
Make sure it’s located before "Android Package Builder":
That’s it, now when you click the run button in Eclipse, the app should always be recompiled from the current Sencha Touch source code in the main folder.
Short test
Open app/view/Main.js and replace the Getting Started content as follows:
html: "<a href=\"javascript:navigator.notification.alert('Congratulations, you are ready to work with Sencha Touch 2 and PhoneGap!')\">Click me</a>",
Run the application and when you click the link, you should get a native alert box:
Finished!
So there you have it, two possibly great frameworks combined! Whether they really are that great – I will find out in my thesis while implementing a sample application using this combination. Then I shall see how it performs on my Huawei Ideos X3 (probably the slowest Android phone available). You can get the finished application at Github.
Some hints: Again, do not change anything in /android/assets/www, but rather edit the code in the main folder
(app.js and anything in app and resources). Mind that we used the command app build testing in our builder – this
is cool for debugging because it leaves the JavaScript unminified. If you want to release your app, you should replace
testing by production.
Note also that PhoneGap might not be available as soon as Sencha Touch loads. I tried it in a painted event and it
was not loaded yet. Since the sencha app build command loads the application to find dependencies, you have to take
care that it can be loaded – using PhoneGap in startup code will produce errors, for example.
And watch out for stupid syntax errors:
Feedback and further considerations
I’m happy to hear any feedback to this article. Just leave a comment or send me a mail. If someone finds out how to create a splash screen that hides when Sencha Touch is loaded, or how to use PhoneGap in startup code, let me know! Cheers!