Enhancing our Android app’s launch performance

karntrehan
Pepperfry Tech
Published in
4 min readNov 4, 2019
Successful launch of Chandrayaan-2 (cc: TIO)

Pepperfry’s Android app is launched 1.3L+ times a day by our happy customers. This number is growing daily. A quick and smooth launch makes sure our users can quickly perform the action they intend to perform. A slow or delayed launch can lead to frustration for our users and a potential drop-off. In August 2019, we ideated and set up a plan to ensure a faster launch for our Android app and have enhanced our launch performance by ~40%.

Objectives

We set out objectives to enhance the launch performance. Most of these were user experience objectives while some were code enhancements.

  • Quick first draw — The user should see our splash screen as soon as the launcher icon is clicked
  • Quick homepage load — The user should see the home screen within the first second of clicking the launcher icon
  • Secure — As soon as the app is launched, user credentials should be sent to an API to validate the session, validate the client, download configuration info as well as show an app-update dialog (if necessary)
  • Lazy load resources — All the analytics libraries, networking clients & dependencies should be initialized only when they are used for the first time

Strategies

Following are some of the strategies we use to achieve the above objectives:

Splash Theme

This was inspired by Ian Lake and his very popular (alas, now deleted) Google+ post to have a quick first draw for your Android app.

When you launch an Android app, the system loads the resources and readies the binaries amongst other things. While this is being done, a blank (white or black) screen is shown to the user. In the latest devices, this phenomenon is not very prominent, but in older / slower devices, the blank screen may be visible for up to a second. We wanted to avoid this.

The idea is to create a SplashTheme having your splash screen as a background image.

<style name=”SplashTheme” parent=”Theme.AppCompat.Light.NoActionBar”> 
<item name=”android:windowFullscreen”>true</item>
<item name=”android:windowBackground”>@drawable/splash_bg</item> </style>

Attach the SplashTheme to your Activity in the manifest.

<activity
android:name=”.main.MainActivity”
android:label=”@string/app_name
android:theme=”@style/SplashTheme
/>

As soon as your activity is ready to display, change to your normal theme.

class MainActivity : AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
setTheme(R.style.AppTheme);
super.onCreate(savedInstanceState);
}
}

This shows a splash screen to our users as soon as they click on the launcher icon. The splash screen is removed as soon as the binaries are loaded & the system is ready to show the first screen.

Cache home page data

As we wanted to display the home page as soon as possible to the user, we decided to cache the home page data, load data from cache first and then refresh from remote. We evaluated the following caching strategies:

  • Cache API responses to a relational SQLite database: This was considered an overkill for such a simple use-case. We did not need any of the relational capabilities of SQLite and the response structure keeps changing requiring us to write migration scripts in the future.
  • Cache API response as string to SharedPreferences: This was better than a relational database. Although, Android performs certain threading locks and analysis before reading and writing data on SharedPreferences. This was impacting our launch times by a few hundred milliseconds.
  • Cache API response as a string to a local file: This was the solution we went ahead with. The local file system was extremely performant as well as secure compared to SharedPreferences. We serialized and de-serialized our homepage response using GSON. Saving files on internal storage also kept them secure.

Using ListAdapter with its AsyncListDiffer, allowed us to refresh only those home page elements which had updated from remote, thus enhancing performance.

Distribute API

All our launch APIs were audited. Legacy and unwanted keys were removed. The /config API, which is the first API to be hit to verify clients, was made extremely lightweight. This ensured a response within 100ms. The server team also improved the caching mechanisms at their end.

We also distributed concerns like cart and wishlist into individual APIs. These APIs ran in parallel post the first draw, thus reducing the initial load time. Parallel reactive streams (RxJava + RxAndroid) were used extensively to make this performant.

Async load libraries

It is not uncommon for analytics libraries to be initialized in the App’s Application class. Some of these libraries may perform blocking operations thus impacting our launch time, such bottlenecks were identified by us. Wherever possible, we initialized libraries post the /config hit. This made sure the first draw and home load from the cache were complete. The user was seeing the home screen before the libraries consumed resources to initialize. Our initial dependency graph was also thinned down. Elements were initialized lazily, giving us a ~100 ms boost.

Final gain

As mentioned in the introduction, we saw a ~40% enhancement in the homepage render time for our users and we were also able to reduce initial memory usage. This helped our users get a better experience out of our Android app.

What’s next?

We want to make sure the homepage data saved to the user’s file system is secure. For this we are evaluating Jetpack’s security-crypto. We are also being very careful of what is being added to our initial dependency graph and Application class. Wherever possible, things are lazy-loaded.

Want to help us enhance our load performance further? Interested in working with us in one of the fastest growing startups in India? We are hiring!

--

--