Thursday, 9 February 2012

I was trying to make a Sencha Touch 2 app with some CSS3 animations in it performing reasonably on the Galaxy Tab with Android 3.2. When running the app in the Android web browser there were two major problems: the view was flashing after every transition quite unpleasantly and some bars of the chart were appearing/disappearing randomly, there seemed to be some problem with the page refresh.

I tried creating a simple native Android app and running the Sencha Touch app in a naked WebView. That didn’t work at all, the native WebView is more or less useless without an additional tweaking.

Finally, since I wanted to have an APK as an end result anyway, I’ve wrapped the app in PhoneGap 1.3. Immediately, it started behaving much-much better, the old problems were gone and the performance of the animation was close to native, and close to that on the iPad.

All this at the cost of a new major problem though: the very first interaction with the app after it had started was always causing a blank web browser window to appear, with about:blank URL in it. Since this window covered the app, the impression was like the app suddenly disappeared, like if it had crashed. The app was running just fine though, and after retrieving it from underneath the web browser one could appreciate all the benefits of PhoneGap wrapping.

While I was investigating this problem, PhoneGap 1.4.1 was released, but it didn’t fix the ‘about:blank’ problem.

I was able to find the reason for the about:blank window in the source code of PhoneGap. It is a minor bug that wouldn’t matter in most normal circumstances. There is a bigger problem that causes the minor bug to manifest, and I still don’t know whether that mysterious bigger problem caused by Sencha Touch or by Phone Gap. However, I fixed the minor problem, created a custom PhoneGap build, and everything works just fine now.

Below, I will describe the details of my investigation. I will also submit this discourse to Phone Gap in a hope that they will fix the bug in one of the coming releases.

I was debugging on a device, reproducing the problem and checking what LogCat says at that time. The message that corresponded to the blank browser window appearance was quite obvious:

This means that some process has sent to Android an Intent requesting to view a specific URL, and the URL was specified as “about:blank”. The natural Android’s reaction to such a request is to start its web browser and to display in it a blank page. The next question was: which exactly code sends such an Intent? I obtained the source code of Phone Gap and searched through it. There is only one place in the code where such an Intent is created, in DroidGap.java:

A question that could be asked here is why the onDestroy() method of the DroidGap activity is invoked at the first interaction with the app, and this is exactly where the mysterious bigger problem manifests itself. Since the app continues to run successfully, I guess that another instance of DroidGap is being quietly created and destroyed behind the scenes. It might have been totally unnoticed if not a minor bug that I had discovered, so let me concentrate on the bug for now and report the other findings later.

The problem with PhoneGap is that the above line of code doesn’t actually achieve what it is expected to achieve according to the comment, i.e. it doesn’t load a blank page into this.appView, where this.appView is a reference to the instance of the WebView that is used by PhoneGap to display the app.

This is because the appView has a WebViewClient attached to it (GapViewClient or CordovaWebViewClient depending on the version of the source code). That client has the shouldOverrideUrlLoading() overriden, which means that each time the WebView is about to load a URL, that method is invoked first. If the method returns false, the WebView proceeds with loading whatever URL was given to it, otherwise it does nothing and it is up to the developer to do any appropriate coding to react to the URL.

If we look inside the shouldOverrideUrlLoading() method, we’ll see that it checks for some special cases, like URLs beginning with ‘mailto:’, ‘sms:’ and so on, but ‘about:’ isn’t one of those special cases. For those URLs that aren’t special, the WebViewClient does the following:

Basically, it creates an Intent, attaches to it the URL (which the WebView was intending to load) and throws the Intent to the Android OS to handle (and so open a blank browser window). Then it returns true which tells the WebView something like “don’t bother handling this URL, I’ve already done everything that was needed”.

So as a result of this line of code:

this.appView.loadUrl("about:blank");

WebView does nothing while WebViewClient passes “about:blank” to Android to handle. Thus the developer’s intent to “load blank page so that JavaScript onunload is called” is circumvented, and this is a bug.

To fix the bug, I’ve added to shouldOverrideUrlLoading() the following one-liner:

else if (url.startsWith("about:"))
{
return false;
}

So that for a URL like “about:blank” WebViewClient tells WebView: go on and load this URL, I don’t care.

I’ve created a custom PhoneGap JAR with this patch and it works fine, the dreaded blank page doesn’t appear anymore.

Now a few thoughts on what’s going on behind the scenes, why a DroidGap Activity is being destroyed invisibly. Here is the complete logging that accompanies the appearance of the problem:

When I tried to find out which code could be calling startActivityForResult() on DroidGap, I’ve found myself digging into the Capture class and methods like captureImage(). So it looks like some failed attempt of capturing a screenshot. But why would it happen when the only thing I was trying to do is to press a button (any button) on the app right after its startup?

About Me

It's a secret but I have two very different lives. In one life, I am an experienced Mobile Developer with a substantial experience on both iOS and Android platforms. I have many years of Java EE and Web Development in the past. Also, I have published two books, one of them about Apache Tapestry (still in print), the other one about Apache Batik. I've got MSc with Distinction in Enterprise Systems Development from Glasgow Caledonian University and work as a Principal Consultant for Keytree in London.

In my other life, I am a devoted student of those areas of knowledge that come from our Hermetic heritage, predominantly Astrology, Numerology and Alchemy. In this second life, the biggest achievements are my website at www.lunarium.co.uk and a bunch of iOS and Android apps that is constantly being expanded and upgraded. I am also studying towards MA in Cultural Astronomy and Astrology at the University of Wales Trinity Saint David.