So, say we have a piece of rich text, most probably in the form of HTML (e.g. a simplified HTML made for mobile reading), what kind of widget is best used to display it? How do we achieve the flexible yet rich reading experience from popular mobile readers like Readability, Pocket, or Materialistic (!)?
In this article, we will explore two very popular and powerful widgets that has been available since API 1:
WebView, for the purpose of rich text rendering. The article does not aim to provide a comprehensive comparison, but rather touches on several critical desicion making points.
For the sake of comparison, let’s assume that we are given the task of displaying the following image-intensive article, styled to specific background color, text size and color. We will first try to use available APIs in
WebView to render the given HTML in the same, comparable way, then analyze their performance: memory, GPU and CPU consumption. Example code can be found here.
First let’s quickly get through the basics. By the way they are named, it is quite obvious that
TextView is meant for text rendering, while
WebView is for webpage rendering. With proper use of their APIs however, we can quickly turn both of them into flexible widgets that can render rich text with images.
Html.fromHtml(String, ImageGetter, TagHandler)to parse HTML string into
WebView.loadDataWithBaseURL()to load HTML string into
TextVew, with default support for
Spanned, an interface for markup text, allows very fine-grained format options over any range of text. It also exposes a bunch of styling attributes, e.g.
textAppearance, and APIs for controlling text appearance. Check out Flavien Laurent1’s and Chiu-Ki Chan2’s excellent materials on advanced uses of
When it comes to rich text however,
TextView shows certain limitations that we may want to weigh up before considering using it: it only handles a limited set of HTML tags, which should be sufficient in most cases; and we have to handle fetching embedded remote images by ourselves, via an
ImageGetter instance; and intercept hyperlinks by using
Meant for HTML display,
WebView supports most HTML tags out of the box. We already know how to use a
WebView to load a remote webpage with
WebView.loadUrl(), but it can also load a local webpage as well: by wrapping HTML string inside a
<body> block and loading it via
WebView.loadDataWithBaseURL(), where base URL is
WebView supports zooming (need to enable), and handles images and hyperlinks by default (of course!).
Now let’s try to style
WebView to render the example article on a teal theme, with some standard paddings around it. We can see that both of them are capable of rendering the page in pretty much the same way, with the exception of
TextView ignoring the
<hr /> tag it cannot handle.
TextView provides many attributes and APIs out of the box for styling,
WebView does not provide public APIs for styling its HTML content. However with some basic CSS knowledge, one can instrument given HTML with CSS styles and achieve desired styling as above. We need to be careful on the conversion from CSS metrics to Android metrics though.
Using the above techniques to style
WebView to display the sample HTML content yields the following performance statistics.
As seen from the performance monitor screenshots,
TextView consumes significantly more memory than
WebView, as it needs to hold bitmaps from all loaded images once they are fetched, regardless of whether they are visible on screen.
The example article has 7 images of various sizes with a combined file sizes of 2MB which would become bitmaps in memory. The amount of memory needed depends on how we sample or resize the fetched images, but all of them will need to be in memory at the same time regardless. If we have an article which holds an arbitrary number of images, we may run into the dreaded
OutOfMemoryException very quickly. Thus for this use case,
TextView is a clear no go.
On the other hand,
WebView historically has been optimized to be memory efficient. Under the hood3 (highly recommended read!), it loads content into tiles visible on screen and recycles them as we scroll, resulting in incredibly low memory profile. However, it needs to use more GPU and CPU to process and draw those tiles into pixels on the fly, probably explaining why it consumes more GPU and CPU than
So the trade-off here is between memory versus CPU & GPU consumption.
Applying above techniques to style
WebView or handle images in
TextView does not come for free. By ‘preprocessing’ our content for rendering, we inherently add certain inital delay to our user experience. A video is best demonstrates this point:
This initial delay may vary depending on devices. On high end devices it may not be noticeable, but we all know how many low end Android devices are around! Using a progress indicator when applicable would surely smoothen the experience.
Another side effect of
WebView is that users may see some pixelated effects while scrolling as the tiles are recycled to render new content.
Bottom line and gotchas
Both widgets have its highs and lows when it comes to rich text rendering. For arbitrary HTML, my choice would be to favor
TextView for its low memory consumption and native support for HTML content. Basic HTML and CSS knowledge would be needed, but knowing them will benefit you anyway. If the HTML content is known to be limited to certain tags without images (e.g. forum posts),
TextView would be a more sensible choice.
The article explores a simple layout where both widgets occupy the whole screen. Things may change in a much more complicated way when we place them in a layout hierarchy together with other widgets. Try it out and profile to see what works for you!