Hi, I'm Andrey and I'm an engineer on Google News team.
I'm here to talk about some things
we learned about building performant web UI
while building the Google News phone
and tablet mobile web apps.
I'll describe how we worked around
the shortcomings of mobile devices
and took advantage of modern browser features
and ultimately delivered engaging and fast news
experiences.
Mobile devices are rapidly becoming the dominant platform
to access online content and services.
Rapid growth of mobile devices present a great opportunity,
but also come with their own set of technical and product
challenges.
Technically, mobile devices generally
suffer from slow and unreliable networks, limited memory,
relatively weak CPU and GPU, and, of course, limited screen
size.
At the same time, most mobile devices have modern OSs
and have browsers which are HTML5 compliant with features
like local storage and touch events.
This opens new ways to create engaging web apps.
One of the most prominent challenges of phones
is slow network connections.
This means that phone apps should limit data consumption
to as little as possible on the connection.
Now, this may prompt developers to retrieve
as little data as possible in one data connection
and make frequent data calls.
This approach could backfire, as opening
a connection is quite expensive and time-consuming.
So a balance needs to be maintained
between data transfer on one connection and opening multiple
connections.
On Google News, we balance this by getting all the textual data
of the phone web app in one call to the server.
Images are fetched later on-- as they are not
critical to the news reading experience--
as are various scripts and resources which
are not immediately needed.
Another workaround to slow connections
is to optimize data access around product usage patterns.
In Google News, we try to predict
what the user would do next and prefetch that data.
This means we can show content to users
as quickly as possible.
For example, when a user swipes to a section,
it's a good indication that user could
swipe to the next section of our app as well.
In that case, we prefetch the next few sections
while the user is active on the current section.
Another technique that we use is to fetch
partial concept of a page beforehand.
For example, we retrieve the first article
of all news stories while fetching a section page data.
When the user navigates to a story page,
we display the first article immediately
while fetching other articles on the news story.
We'll also show a silhouette instead of an empty white space
while fetching the rest of the story page.
This gives users some content to interact with immediately
and indicates to the user that more content
is being retrieved.
Static resources such as JavaScript, CSS, and sprites
can be stored in local storage.
Thus, the application will avoid making
HTTP HEAD calls to verify that the cached browser
version of the file is good to use.
This saves a few round trips to the server.
So these are some of the transport layer considerations,
but what about mobile browser level optimizations?
Well, the [INAUDIBLE] rendering of that
by browser internals in the CPU and GPU
have many more deficiencies compared
to typical desktop or laptop machines today.
You must be careful when manipulating and accessing
properties of DOM elements, as it
may result in full or partial screen
delay in the [INAUDIBLE].
Use Chrome Dev Tool's timeline view
to see what the browser spends time doing frame by frame.
And you can turn on various experimental settings that
show internal drawing and [INAUDIBLE] of the browser.
When moving HTML5 elements on the screen--
especially in the response to user gestures-- use HTML5 3D
transforms.
This will offload [INAUDIBLE] processing to the GPU,
resulting in a much smoother experience.
GPU memory is quite limited, so try
to move only the elements which are visible
and hide the invisible sides of elements.
For elements that are about to appear
on the screen-- like when a pane is about to be swiped
onto a screen-- it's very helpful
to bring them into the DOM structure well ahead of time.
And make them renderable, meaning, you
need to set CSS display property to something other than none.
Browsers are typically capable of performing layout and pane
of several screen elements on a separate thread,
often prioritizing among different elements based
on distance to visible viewport.
If you absolutely need to animate something
with JavaScript, use the RequestAnimationFrame API
callbacks to synchronize DOM changes with the vsync.
Everything we've talked about so far
is also good practice when developing other non-mobile web
applications.
But one key feature that singles out mobile applications
is that they are controlled through gestures.
Natively, browsers support vertical swipes
through the vertical scroll of the main body element.
But if you want your application to respond
to horizontal swipe or other gestures like pinch,
you'll need to process raw touch events in JavaScript
and animate appropriate parts of the UI.
To do so, the touchstart event should
be processed in the consecutive touchmove.
And ultimate touchend events should also
be processed in this JavaScript.
And this is where it gets tricky.
For UI to appear smooth, all transitions
should run at about 60 frames per second
which leaves about 60 milliseconds per frame.
The main thread that's responsible for reporting
and executing application specific touchevent handlers
must complete script execution and browser
paning within those 60 milliseconds.
What this means is that code in touchstart-- and especially
touchmove handlers-- should complete
in no more than a few milliseconds.
For comparison, full-screen layout in pane
might take 20 to 30 milliseconds resulting
in several skipped frames.
The thing that allows 40 panes to be quick is CSS 3D
transforms, which move elements on screen as raster images
rather than trying to repane DOM structure.
Once you've optimized touchevent handlers,
you can do browser level optimizations
to streamline browser event processing
and reduce overhead associated with event DOM propagation.
One way to achieve this is to listen to all touch events
on a single top-level DOM element in capture mode.
This will result in no DOM event propagation chipping
valuable milliseconds from touch event
processing on the main thread.
In our phone web app we wrote a small framework
that did just that.
It processed raw touch events at the top-level element,
converted these touch events to gestures-- such as drag-left,
drag-right, up and down-- and routed these gestures
to the appropriate subcomponent of the application based
on subcomponent reported [INAUDIBLE],
for which it should be notified of gestures.
The last topic of this talk is phone versus tablet UI.
A common debate is how different phone and tablet UIs should be.
One school of thought is to have responsive websites
where a site is automatically adjusted
to the available screen size and capabilities.
In our experience, this approach doesn't work very well.
First, a UI that adjusts dynamically results
in complicated code and limits optimization choices.
But more importantly, what we have found
is that phone and tablet consumption
models are very different.
Phone consumption happens more frequently in a day,
but a session lasts only a few minutes.
This is what we call the "snacking" mode
of news consumption.
This means it is much better to have an information-dense
and more summarized view of news.
Whereas tablet usage is very much
limited to morning and evening hours
with the user interacting for an extended period of time.
So it made much more sense to provide in-depth reading
choices on tablets.
We hope this talk will help you avoid some common pitfalls
of web development on mobile platforms
and will inspire you to code slick and fun web
applications for many users to enjoy.
Thank you and see you at news.google.com.