Android is a huge ecosystem, with more than 1 billion active users spanning thousands of different device models. People who use Android have an incredible amount of choice, with significant variations in speed, feature set, and cost. Screen size is the most obvious variable — popular Android devices span from 240 x 320 to 1080 x 1920 pixels, a 27-fold difference in pixel count! In some emerging markets, users must also contend with unreliable mobile networks. All this means that supporting Android users is both a technical challenge — and an opportunity for mobile developers who want to reach the entire world.
At Instagram we’ve spent the last year reimagining and redesigning our Android app to work better for our users, no matter what phones they use or where they are located. We shared details about this effort for the first time at this week’s @Scale conference.
We focused our work on Android on two key areas: design, and startup time.
We redesigned Instagram for Android six months ago with three main goals in mind: making the app faster, more beautiful, and more screen-size aware.
"Flat design" has taken hold in the mobile world over the past few years. Android’s "Holo" theme, Windows Phone, and iOS7 all ditched complex gradients and shadows for solid colors and flat images. Flat design looks great on phone screens, but there is an even more important reason why it has taken hold: performance. Flat design is all about doing less — stripping away UI elements and letting the content speak for itself. Drawing solid colors on the screen is faster and more memory efficient than loading and displaying gradients from image files on disk. Simplicity means less work for the phone’s hardware, hence a faster app.
With these goals in mind, we rewrote every single screen in the app. We gave Instagram on Android a beautifully flat makeover, trimmed unnecessary UI to give you more space to view photos and videos, and focused relentlessly on doing less in order to make the app faster.
The most dramatic UI optimization happened in the photo- and video-capture and editing flow. We rewrote the original layout flow to be more screen-size aware. To do this we divided screens into four buckets, based on aspect ratio and DPI, and used a condensed layout on only the screens in the smallest buckets. At the same time, medium- and large-sized screens received an expanded layout that better utilized the available pixels. The result is that most Instagram’s Android users now experience a more ergonomically friendly flow, with all the editing and camera controls easily within reach of your thumb.
One of the techniques we use widely through the app is “asset tinting,” the ability to colorize assets programmatically. In a flat world, all of our assets are simply shapes, and we can change their colors at runtime. This allows us to eliminate separate assets for different UI states. Asset tinting is touted as a new feature in the upcoming Android L release, but it’s actually been possible in all versions of Android. You can apply a ColorFilter to a Drawable or an ImageView, and it will change the rendered output. We keep a static cache of immutable ColorFilter objects and reuse them all over the app.
In the end, we were able to dramatically reduce the number of assets needed display the Instagram UI when the app starts. We went from 29 assets to display the title bar and tab bar to 8. It turns out that not loading and decoding all those assets on startup gives you an awesome speed win; this alone reduced the app startup time by 120 milliseconds across devices (an improvement of roughly 10-20%, depending on the device). These gains were felt all across the app — for example, user profiles displayed up to twice as fast because of this simplification.
With this new flat design, and using the techniques described above, we were able to ship a smaller APK with fewer images. We cut the total number of assets in the app in half, even while adding xxhdpi assets. Along with other optimizations, the APK we shipped post-redesign was half the size of what it had been a few months before. This is a huge benefit for users who pay for data by the kilobyte and must wait for the app to download over really slow networks.
Users don’t want to wait forever for their apps to start up. This is especially important on Android, where less powerful phones will kill apps more often under memory pressure, making the impact of a long startup time even more painful.
We managed to cut the Instagram app start time in half over the past year on Android, to the point where it’s now one of the fastest-starting apps on the phone. Instagram now starts up and is usable in less than 0.5 seconds on a Galaxy S5, and in only 1.5 seconds on a Galaxy Y, an older device that has been popular in emerging markets.
To achieve these gains, we spent a lot of time profiling the app, both using the Android TraceView tool, and manual timing statements in the code. We made a lot of small improvements, like rewriting inefficient JSON parsing code and lazy-loading components that weren’t really needed for startup. There were two areas that required some creativity.
The first was in managing “heavy” app-wide singletons: services like our image cache, video cache, and http client. These need to be initialized for the app to work, right? Well actually, they don’t — we can start the app and interact with it without issuing network requests or showing any photos or videos. This means that these services can be lazy-loaded. However, we don’t want to complicate the programming model by having these objects be “null” during startup. So we initialize these objects in two steps. We create them on the main thread as we used to, but leave them in an uninitialized state, just enough so that the public API works. Then, when we spin up separate threads to actually load images or load content from the network, we finish initialization by doing the heavy lifting — loading SSL certificates off the disk, or opening and reading the cache journal file.
The second thing we found was that our News page was really slowing down app startup. The News page shows you who liked and commented on your photos, and we load it at startup so that you can see your new activity immediately. News was originally implemented as a webview, and after profiling we were surprised to find that it was creating a lot of threads on startup, stealing time from the main processor. We realized that we had no control over how the webview manages important system resources. It spins up its own networking stack, manages its own image cache — a lot of duplicated work with the rest of the app. To fix this, we converted News to be a native view. This gave us enough control to be able to delay loading News until after main feed is loaded, and to share the network stack and image cache with the rest of the app.
All of these efforts combined led to a much more usable Instagram for Android, which people have been enjoying for the past 6 months. Simplicity is a core principle for our engineering and product teams, and we’ll continue to push hard on efforts like these to make the Instagram experience as fast and beautiful as possible for all of our users.
By Tyler Kieft