App Startup Time
Cold, Hot, and Warm App Launch Times and their impact on user engagement
The agenda of this article is to understand the difference between these three terms mentioned in the title of this article, benchmark time, some stats explaining user engagement, and last but the most important best practices for each type of app launch.
Let’s talk about these launch types in detail
Use Case
Hold your android mobile, and clear everything from app stack (minimized) apps. Click on your app icon from home. Everything will be initialized from zero.
What does OS do for us? After the app process creates your activity, the activity performs the following operations:
- Initializes values.
- Calls constructors.
- Calls the callback method, such as onCreate(), appropriate to the current lifecycle state of the activity.
Typically, the onCreate() method has the greatest impact on load time, because it performs the work with the highest overhead: loading and inflating views, and initializing the objects needed for the activity to run.
Use Case
Hold your android mobile, and clear everything (minimized apps) from the app stack. Click your app from the app launcher and make a fresh app start. This will bring your app to the foreground. This was so far a cold start. Now press the home ◯ button from your mobile, and your app will go into a paused state. Now press the app switcher button ▢ and bring back your app to the foreground. This process will bring back your app to the last state where you left without reinitializing any of the app assets.
During this process with your app, if activity got killed by OS due to onTrimMemory or any other reason, it will not be considered as HOT start
What does the OS do for us?
A hot start of your application is much simpler and lower-overhead than a cold start. In a hot start, all the system does is bring your activity to the foreground. If all of your application’s activities are still resident in memory, then the app can avoid having to repeat object initialization, layout inflation, and rendering.
However, if some memory has been purged in response to memory trimming events, such as onTrimMemory(), then those objects will need to be recreated in response to the hot start event.
A hot start displays the same on-screen behavior as a cold start scenario: The system process displays a blank screen until the app has finished rendering the activity.
Hold your android mobile, and clear everything (minimized apps) from the app stack. Click your app from the app launcher and make a fresh app start. This will bring your app to the foreground. This was so far a cold start. Now press the home ◯ or back ◁ button from your mobile, and your app will go into paused/stop state. You leave your device and forget you were working with an app or you were watching a movie that was buffered 50%. Now there are two cases. if you come back after pressing the back button, your app will restart, that’s obvious. What will happen if you pressed the home button ◯ last time? It may start from the last case (ideally) but what if I tell you when you were away or started a new app from the app launcher, the OS was running short of memory and acquired some memory from your paused app. It will force your current screen to reinitialize.
So what happened in the background? Some of the resources were initialized and a few were already in memory when your app came back into view (Foreground). This state is in between the Cold and Hot app start. So it is called a Warm start ;).
What does the OS do for us?
A warm start encompasses some subset of the operations that take place during a cold start; at the same time, it represents more overhead than a hot start. There are many potential states that could be considered warm starts. For instance:
- The user backs out of your app, but then re-launches it. The process may have continued to run, but the app must recreate the activity from scratch via a call to onCreate().
- The system evicts your app from memory, and then the user re-launches it. The process and the activity need to be restarted, but the task can benefit somewhat from the saved instance state bundle passed into onCreate().
Best benchmark times for each launch time?
Google says the ideal benchmark time should be
- Cold startup takes 5 seconds or longer.
- Warm startup takes 2 seconds or longer
- Hot startup takes 1.5 seconds or longer.
App Not Responding
ANR Occurrences
When Your Activity has too much loading on a main thread like
⦁ Network request
⦁ Too much API calling
⦁ Too many calculations
⦁ Slow loading
⦁ The main thread blocks more than 5 seconds
⦁ User Input(on button clicks or key presses) takes more than 5 seconds
⦁ Broadcast Receiver has not finished its processing and the apps goes into foreground states
⦁ Memory Leaks
Techniques due to which you can find out which of these causes is causing your ANRs
–Strict mode
StrictMode is a developer tool that detects things you might be doing by accident and brings them to your attention so you can fix them.
StrictMode is most commonly used to catch accidental disk or network access on the application’s main thread, where UI operations are received and animations take place. Keeping disk and network operations off the main thread makes for much smoother, more responsive applications.
public void onCreate() {
StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork() // or .detectAll() for all detectable problems
.penaltyLog()
.build());
StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.detectLeakedClosableObjects()
.penaltyLog()
.penaltyDeath()
.build());
super.onCreate();
}
Strict Mode can help you decide what should happen when a violation is detected.
-Enable background ANR dialogs
You can enable ANR Dialog of a device from settings(Developer Options) to see when an ANR occurs.
-ANR Diagnosing using Android Profiler
You can use Profiler to get a trace of your running app while going through the use cases and identify the places where the main thread is busy.
Memory Leak can be recognized by the profiler
After analyzing, you have to free all the objects in OnDestroy Method to avoid ANR’s.
Finding Out ANR Solutions:-
Using Worker Threads:-
Example:-
class ExampleRunnable implements Runnable {
int seconds;
ExampleRunnable(int seconds) {
this.seconds = seconds;
}
@Override
public void run() {
for (int i = 0; i < seconds; i++) {
if (stopThread)
return;
if (i == 5) {
/*
Handler threadHandler = new Handler(Looper.getMainLooper());
threadHandler.post(new Runnable() {
@Override
public void run()
buttonStartThread.setText(“50%”);
}
});
*/
/*
buttonStartThread.post(new Runnable() {
@Override
public void run() {
buttonStartThread.setText(“50%”);
}
});
*/
runOnUiThread(new Runnable() {
@Override
public void run() {
buttonStartThread.setText(“50%”);
}
});
}
Log.d(TAG, “startThread: ” + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
You Can use Thread to perform all loading operations like progress bar loading,network calls and fetch results to the main UI Thread to Avoid ANR.
Memory Leaks Solution:-
You should not:
- Avoid using static variables for views or context-related references.
- Pass a context-related reference to a Singleton class.
- Improperly use Context. Use applicationContext() instead of activity context or view context when it’s possible. For example, for Toasts, Snackbars.
- Use a weak reference of the context-related references when needed.
- Static and View References
Just forget about static when you are working with context-related classes. You don’t need it in Activity for Views, and you don’t need to hold Context everywhere.
If you still can’t manage your logic without such a principle, your architecture or pattern can be wrong for that scenario. Use official guidelines and look into Dependency Injection or app\data\domain modules.
/*
* -> Static and View References
* Bad practice: Static context-related variables
*/
companion object {
private lateinit var textView: TextView
private lateinit var activity: Activity
}
- Unregistered listeners
Listeners and receivers, such as BroadcastReceiver, LocationListener, Bluetooth and BLE listeners, have a strong reference to Fragment or Activity, and they hold them until you unregister them.
Carefully check your onCreate (onCreateView), onStart, and onResume methods. These lifetime methods typically have registered events. For each registered event, you should have unregistered opposing methods onDestroy, onStop, onPause.
Unregistered Receivers. Unregistered Receivers
–Inner classes and anonymous classes
Here we need to notice that the outer class should never have a static reference to the inner class. The inner class should never have a hard reference to the outer class, especially when talking about context-related classes.
Anonymous classes, such as Handler().postDelayed() or classes that emit a new thread (Runnable, AsyncTask), should stop and close this thread before being destroyed. Take care of every thread and resource that you have created.
–Singletons
The memory leak could happen when we initialize the singleton from the activity and pass a long-lived context-related reference to the singleton constructor when we initialized it. Singleton class will hold a reference to Context after destroying the initial resource.
Try to hold applicationContext() instead, or use Context as a parameter of Singleton methods.
/*
* -> Singletons
* Bad practice: Not application context variable inside Singleton
* Bad practice: Not destroying context inside onDestroy
*/
inner class SingletonClass(var context: Context) {
lateinit var instance: SingletonClass
}
–Bitmaps
Inefficient use of Bitmaps without recycling it. Managing Bitmaps may easily throw OutOfMemoryException.
Join our newsletter
Volutpat vel turpis nulla lorem sed semper. Aliquam sagittis sem libero viverra vehicula nullam ut nisl.