The rivalry between mobile application platforms continues to flare as companies such as Google, Facebook and Apple choose to invest in native apps over HTML5. While HTML5 is viable, few are opting to take on the cross-platform mobile application strategy--Mozilla being one of the few exceptions. However, even though companies are going native, I believe the shift to HTML5 is inevitable. Just as we once moved from native desktop applications to web-based ones, history will repeat itself and HTML5 will be the platform of the future.
In this article, however, I want to discuss an area where HTML5 does not meet the needs of mobile applications--networking. Networking has always been a passion of mine, and it led me to help create the SPDY protocol which is now a starting point for HTTP/2.0, the next revision of our core HTTP protocol that is used worldwide.
If you're writing a mobile game, you probably don't have to worry about networking. But if you're writing a mobile application that integrates with any cloud-based data, such as an interface to a banking application, a mobile calendar, a mobile communicator, or a mobile application to control your car, you're going to need to change the way you think about network programming.
It's worth noting that native applications are no better than HTML5 applications when it comes to the network. All of the topics I cover here will need to be addressed in native applications just as in HTML5 applications. However, I fully expect that HTML5 will eventually adopt primitives that will make it so application developers won't need to worry about these in the future.
For now, when it comes to HTML5, we've gotten comfortable with a set of XMLHttpRequest primitives which supply our asynchronous network loading. These APIs work great on desktops, but only solve about 20 percent of the problem on mobile.
The Problems of Mobile Networking
There are three key problems in mobile networking to consider:
1 High latency
2 Unreliable networks and switching network conditions
3 Battery Life
Most people are aware that network latency is a huge problem on mobile devices. The basic round-trip time (RTT--the time it takes to send a packet from one machine to another and back) on a typical 3G mobile network is about 200ms. This is a sharp contrast to typical round trip time on desktops which are often 40ms or less. The difference, 160 milliseconds, is a small amount of time--less than the blink of an eye (about 300ms)! But these small delays add up. At Google, we measured that a simple web page with a 200ms RTT will load in about 3.5 seconds, while a web page at 40ms RTT will load in just over 1s.
But beyond basic RTT, mobile networks have additional struggles for latency. In order to preserve battery life, all mobile devices put the radio into deep sleep states. And when in deep sleep, it takes multiple round trips just to get to a state where your application can transmit. Given the unreliability of mobile networks, this "radio wake up time" can easily exceed 2-3 seconds. Even in the best case, users are typically waiting for about a half a second before an app can even contact the network.
Unreliable Networks and Network Switching Costs
The second problem with mobile network programming is that the networks are unreliable. When using mobile devices on the go, we commonly run into "dead zones" where the network simply doesn't work. This condition can either be permanent or transient, but for application developers this means that critical network operations will simply not go through. Trying to save a document to the cloud? You'd better have a backup in place so that you don't lose your data.
In addition to out-of-coverage areas, mobile devices also have expensive network switching costs that are transparent to the user. Mobile devices today are equipped with multiple network radios--including 3G and WiFi. The devices will commonly attempt to switch networks, but they don't really know when it is the "best" time to do so. As one network signal fades, it can take anywhere from a few seconds to minutes before the device realizes it needs to switch networks. During this time, the user is left in the lurch with non-responsive applications. It's frustrating, but mobile application developers can mitigate this.
A third problem with mobile networking reliability is the network carriers themselves. When your mobile device connects to the network, it establishes a public IP address through the carrier. The carrier then uses large NATs to map between the external network and the carrier's internal mobile network. Unfortunately, TCP/IP was never really designed for NAT support, so the carrier is forced to eventually time out your NAT. If you attempt to use a connection after the NAT has been torn down, your client will simply....... hang..... Someday, IPv6 may solve this problem, but for now, we're mostly still using IPv4 (test for yourself here!).
The simple solution to mobile networking might be to use past solutions for TCP/IP--just use a "keepalive" and keep your connections open, and when they get torn down, open a new one. This works great if you're power-insensitive, but on any mobile network you'll easily drain the battery in no time. I've tried many forms of this, and the best I've seen is a drain of about 5 percent battery per hour. That's as bad as burning the CPU, running high-precision GPS, or leaving the screen on all the time. Most users won't tolerate it.
Closing connections and letting the network sleep is great for your battery, but further exacerbates the latency problem described earlier, as when you next use the network, you're going to reconnect everything.
Solving the Problem
Now that we understand that mobile networking is extremely slow and variable, we can formulate some solutions for mobile apps. I have an underlying assumption here, which is that although the mobile networks have serious problems, users still expect the same performance they've had with their desktop applications. They don't realize (nor should they!) that they're disconnecting from WiFi right now, or that they're in a no-coverage zone, or that they're experiencing 20 percent packet loss. Your application still needs to work and this is where the HTML5 networking APIs fall short.
To make this work for users, we need to present some smoke-and-mirrors. We need to tell the user that everything is done, while decoupling the user interaction from the state of the network request.
Network operations will be in one of two forms:
- I'm trying to read data from the network
- I'm trying to sync data to the network
In the case of reading data, applications should:
● Warm up the network as early as possible. Instead of waiting until the user taps "search", applications should connect to the servers early and pre-warm connections while the user is still tapping about the screen.
● Cache heavily. Caching policies will vary from app to app, but redundant requests are common in most applications. Make sure you cache as much as you can to avoid network interactions where possible.
● Build your own retransmission policies. If you're not getting a response from the server, detect a timeout quickly and retransmit. Modern browsers do this internally when loading web pages, but XmlHttpRequest does not. Now that you're in control of the network, you need to do it too. Retransmission should be smart and exponential. It's okay to try a retry or two at 1 second intervals, but if they continue to fail, back-off exponentially.
● Observe network change events. Desktop applications generally just assume the network is online. Prior to mobile, HTML5 didn't expose any network status to its applications. However, knowing whether you're online is critical for setting user expectations, managing the device battery, and understanding when to retransmit. PhoneGap offers a stopgap solution now, and the W3C has working drafts to add this feature to HTML5 in the near future.
● Use as many network connections as you want! Desktop HTML5 browsers have limited themselves to 6 connections per domain. When you're accessing your own network service, you don't need to do this. However, you do need to consider that requests sent in parallel on multiple connections may be processed by your server out-of-order! While out-of-order network requests are relatively infrequent on our fast, reliable desktop networks, they happen all the time in the mobile world.
Writing data to the network is even more difficult. Remember the days when your browser would lose a form submission if the network failed? Browsers have largely solved this problem by caching your form data locally before submitting, and sometimes even persisting to disk across browser restarts. In mobile applications, you need to do the same thing, but more:
● Always save data locally before attempting to send. Build a transaction log so that you can send your data later if the network should fail or your app should be terminated.
● Reflect state to the user as though the operation completed. Rather than forcing the user to wait for your slow network, applications should simply save the work, queue it up, and let the operation complete when it can. This will create races between your clients and servers which you'll need to consider and can be very hard to solve properly!
● Build a protocol for transmitting state to your service. This recommendation may seem controversial--given how much we've grown to love REST in recent years. But once you've resigned yourself to providing a snappy UI that doesn't wait for the network to complete, this is a natural side effect. Your application will need to know what state it is transmitting and keep it in sync with the server, while also resolving conflicts that may be induced if there are multiple clients writing the same data store. Very quickly, you'll realize you're building a small transactional system. Experienced engineers will observe this is no simple thing to get right.
At Twist, we've had to build all of these features, and I'm sure countless other applications have done the same. Unfortunately, the network impacts of various protocols, retransmissions, and network prefetching are subtle, and with each application developer implementing them independently, I'm sure we're destined for some rocky networking ahead. I look forward to HTML5 based APIs which take these problems into account and solve them universally. SPDY has been designed to assist with mobile latency and should offer some improvements which avoid unnecessary network stalls, but your application will still need to be cognizant of these problems too.
Mike Belshe is the co-founder of Twist.