The Pains and Remedies of Android HTML5

Prologue: I’ve written most of this post some months ago and somehow didn’t publish it. Looking at it now, it’s a good reminder of some of the pains I already forgotten. The Android version statistics already changed a bit by now, but, still today and even with the new type of measuring by Google – the most problematic Andorid versions which are 2.2.x – 4.0.x are still running more than 50% of Androids out there. Hence everything here still applies. (note that most of the bugs are in 4.0.x *and not in 4.1.x* and above).
I’ve updated all the stats in the article to reflect the latest published stats.

These issues refer to HTML5 content running inside the native Android browser as well as  inside the native WebView (i.e. PhoneGap and alike)

———————————–

The promise of HTML5 is great, to be able to use the same code base on all clients and even on the server is really compelling.  While iOS has provided what it promise long time ago already – you can relatively easily create compelling HTML5 apps that will run on the iOS. Android HTML5 capabilities are still lagging far behind.

On paper Android 4.0.x (20.6%) was enhanced with many awaited features of HTML5. Similar to iOS 5. For example, Android 4.0.x was added with the important overflow:scroll, but, the Android 4.0 version is flawed. It has many other great features which are, sadly, mostly buggy. In fact this version is a buggy regression to the Android browser and WebView HTML5 capabilities.

It gets much better in Android 4.1, but this version still only holds only (36.5%) of Androids (48.6% including 4.2 & 4.3). Still today the most common version is 2.3.x which holds (44%) and that version can not be avoided. Generally, if you’ll try to push the HTML5 envelop of the Android it’ll probably push you back.

Even with the new and optimistic way of Google to measure Android versions distribution it’s still clear that 2.2.x and 2.3.x and 4.0.x are still massively out there and needs to be supported.

Having said all that, it doesn’t mean you can’t create decent apps with HTML5 that will run properly on the Android. But you’ll have to consider its lacking abilities from the get go. Design the UI as simple as possible, without too many fancy CSS, images, and animations.

I will put here a list of some of the issues I had to go through while adopting HTML5 on the Android, I will keep this list updated.

Canvas:
Pain:
 Android* 4.1 – 4.3* render duplicated HTML5 Canvas
Remedy: None of the parents HTML elements to the canvas should have overflow: hidden or overflow:scroll

Pain: In all Androids and especially 4.x canvas drawing performance are extremely reduced by using canvas effects like shadowColor.
*Remedy: *Try pre-rendering or adding the effects only when needed and/or once in every drawing cycles. For example, in a live drawing app – adding the effects only when the user stops to draw.

Network:
Pain: Android 2.x.x Making PUT (protocol) requests with no body will have no Content-Length header, it’s rejected by some servers/proxies i.e. NGNIX
Remedy: Configure NGNIX to accept it or send a {dummy: ‘data’} in the payload. i.e. $.ajax(‘PUT’, url, {dummy: ‘1’});

Pain: Android 2.x.x PUT (protocol) is cached on some versions of Android
Remedy:Cache-bust it, cache-bust all requests to the server even if it’s PUT.

Content:
Pain: Box-scroll was introduced in Android 4.0.x  but it has numerous issue on that version.
Remedy: Don’t use box-scroll for anything under than 4.1, or use iScroll or similar. The best, most performant, solution is to use postion:fixed for headers and footer and to simulate box-scroll.

Pain: CSS pseudo :active selector is not working on 2.x, working badly on 4.0.x.
Remedy: It is only perfect from Android 4.1 and above, try to use your own implementation using touch events.

Pain: Making fixed content (position: fixed) issues on 2.x.x
Remedy: Works fine only  when the ViewPort is not resizable, use this in the html head:

Pain: Scrollbars shows over fixed content.
Reedy: When using a native shell, scrollbars can be removed using
webView.setVerticalScrollBarEnabled(false);
webView.setHorizontalScrollBarEnabled(false;

Pain: Jumpy text inputs
Remedy (native shell):
Remedy 2 : don’t use *{ -webkit-backface-visibility: hidden} or try to override it with *{ -webkit-backface-visibility:visible !important; }

Pain: Styling text-inputs that has focus
Remedy:http://stackoverflow.com/a/9464837/275333, http://java-cerise.blogspot.co.nz/2011/10/dodgy-double-input-fields-on-android.html

Pain: Android 4.0.x, any tap implementation will not be responsive enough, it will miss a lot of taps. (works fine with all other versions)
Remedy: Essiest will be to revert to clicks on this buggy 4.0.x version

Pain: In Android 4.0.x long press is selecting text, on all other OSs it’s resolved with the css *{ -webkit-touch-callout: none; }
Remedy (native shell): Use this Java snippet http://stackoverflow.com/a/11872686/275333

Pain: Duplicated Input fields on Android 4.0.x. it happens because android uses another native input for fast typing response, it doesn’t work well with scrollable content. (very ugly hack google, if I may)
There are tones of hacks for that out there, most of it doesn’t work or at least doesn’t work good across devices.
Remedy (native shell): If you run in WebView – Don’t put text inputs inside a scrollable iframe or content with overflow:scroll. Putting this in the activity will auto scroll to the text-input (similar to iOS) android:windowSoftInputMode=”adjustPan” – only works on Android 4.0.x, not working on Android 4.1 and above (yeah really).
adjustResize is working on all Androids I’ve tested, but that is less pretty and leads to jumpy inputs on older androids 2.x. adjustResize needs to be on the tags in order for it to work. I do not recommend that as well.
So to summarize the fiasco, adjustPan which give the best UX (similar to the iPhone) is only working on Android 4.0.x.
adjustResize which is still nice in terms of UX can be made to work with all versions of Android, but can cause issues (jumpy text-inputs) for old 2.x
Remedy 2: Put this style on the text input -webkit-user-modify: read-write-plaintext-only; * Not great since it’ll make typing slower, it’ll be up to impossible to enter text on some devices. Swype keyboard won’t work either.
*
Remedy 3:
Shift the input element off the screen, and use the change event to render the text into another element. (this is too cumbersome, try to avoid it)

Misc:
Pain:HTML5 PushState is supported since Android 2.2, but somehow it was forgotten on Android 4.0 – 4.0.2 and some 4.0.3 devices. Told you these 4.0.x are cr*p…
Remedy: Make sure your HTML5 app works well for devices without pushState support. Try a 4.0 emulator.

Pain: Incorrect dimensions, sometimes innerWidth & innerHeight might still read 0 even after the DOM is ready.
Remedy:  Wait a few (~100 millisecond) after the DOM is ready to ask it what’s the window size is.
Remedy 2:Use screen.width & screen.height (you’ll have to calculate the toolbar height)
Remedy 3: get the width/height from the server (using something like wufl)
Remedy 4 (native shell):  Get the size from the native shell.

Pain: WebSockets are not supported at all.
Remedy (native shell): Use this WebSockets PhoneGap plugin. Don’t get bothered by sockets unless you really need to.

Pain: Web Workers doesn’t work at all
Remedy: Who cares..?!
*Remedy 2 (native shell): *Multi threaded, yeah baby.

Pain: Android 2.x misses a lot of scrolls attempt because it’s stuck in touchmove event (error is: “Miss a drag as we are waiting for WebCore’s response for touch down.”)
Remedy 🙁 No real remedy, I’m pretty sure there is no sulotion for that and using something like iScroll won’t solve it either.

Pain: DOM manipulation is extremely slow.
Remedy: documentFragments might help but don’t count on it.
You’re left with tricks, for example, It far smother to change visibility than to add/remove DOM elements.
It’s better to pre-render and just show() or hide() as needed, especially when animations are involved.

Some related links:
PhoneGap vs. Native: Some Thoughts on Going Native
Discussion in Hacker News
These are one year old but still very relevant (sadly)
Regarding point 1: Don’t remove images from the DOM, instead replace the src to a very small image (leason learned by the linkedin mobile team), 2: You can handle that, 3: There are good ways to do caching, 4: These days there’s reasonable debuging tools.

HTML5 for extending the device battery life (PDF)

Some other pains & remedies

* Epilogue: * Everytime I come across a cool HTML5 example and wonder how well it tuns on mobile, I try it on iOS  and mostly like what I see. Only to be disappointed with the way Android native browser run it. And I’m not talking solely about the old 2.x.x androids that mostly run these in an unacceptable way. Even the newer androids with new version of the OS doesn’t play smoothly as the mobile safari or even UIWebView. The only solution to HTML5 on Android at the moment, is to keep it simple, very simple.

When targeting an HTML5 app to run on mobile browsers, one can not assume that  her users will use anything other than the native browser (as opposed to the more capable Chrome for Android, for example). But,  if your running your HTML5 inside a native shell (i.e. PhoneGap) There are few projects that attempt to solve the native WebView problem, by letting us bundle a better webview.
https://github.com/thedracle/cordova-android-chromeview
https://github.com/davisford/android-chromium-view
https://wiki.mozilla.org/Mobile/Projects/GeckoWebView
More on these will follow…

Guy A

Read more posts by this author.