How to speed up android app cold start time by 50%
Many users expect your application fast to load and interact quickly. They can rate slow app poorly on the Play store, especially when they used an old and slow device. Speed up android app startup is very important for avoiding negative reviews.
Understand activity startup process
When the user opens your app, there are many launches of tasks: create process, bind application, invoke content provider, launch activity, service … For more detail, you can read here
There are 3 important phases if you want to optimize cold start time: load dex file, application onCreate, and activity onCreate
Profile app startup using Android Profiler
Android profiler is a very useful tool for detecting heavy app initialization. To using it, edit run configurations, select the profiling tab, and check start recording CPU activity on startup. Then click on the Profile button
You will see a method tracing result like this
Remember while profiling app, Android Profiler makes your app laggy and slower than usual
Navigate to the application onCreate method
Be careful with any library on application onCreate. Maybe it block main thread. For example, I’m using AnalyticSdk and method readSessionConfig
take 612 ms
Solutions:
- Move library initialization to background thread
- Lazy load library. Delay the initialization as much as possible. For example, only init AnalyticSdk when using it.
Some library read Shared Preference or do I/O operations during initialization. It’s a waste of time and maybe slow down your app
Optimize layout hierarchies
Navigate to activity onCreate method
Method setContentView
take ~500ms.Because my layout hierarchies are very complex and need to be simplified. Consider using ConstraintLayout as soon as possible to optimize layout hierarchies
Be careful with ServiceLoader
ServiceLoader is good for Java, but it is really slow on Android devices because of invoking a reflective lookup. We can use R8 shrinker instead of Proguard to optimize ServiceLoader. For example, if you are using kotlin coroutines, maybe FastServiceLoader slow down your app. You can optimize it using R8
Use dagger correctly
If you have a monolithic application and using Dagger, you may face Dagger graph creation performance. For example, when a user launches the app, your app must inject AccountViewModel. AccountViewModel depends on UserManager, UserValidator, UserRepository, UserAnalytics. UserManager depends on ActionLogHelper. ActionLogHelper depends on SearchRepository, LoginRepository, FeedRepository … When AccountViewModel is created, all dependencies (UserManager, UserValidator, ActionLogHelper, SearchRepository, LoginRepository, FeedRepository …) will be created, and cause the application slow to start.
Solutions
- Modularization your app instead of a monolithic app, consider using feature scope instead of singleton https://medium.com/androiddevelopers/a-patchwork-plaid-monolith-to-modularized-app-60235d9f212e
- Use dagger lazy for heavy object https://dagger.dev/api/2.11/dagger/Lazy.html
- Be aware of your app structure. All class should be single responsibility and should not contain unrelated things
Minimum DEX file size
The bigger and the more dex file inside APK, the longer load. If your app contains 65k+ method, multidex will be required, cause performance impact on Pre-Lollipop device. Consider using R8 shrinker or Proguard to cut off the unused method.
Using R8 shrinker instead of Proguard.
In my case, when using Proguard, there is a total 80k method but when using R8, only 60k method in APK dex file. To see how R8 work better than Proguard, please read Digging into D8 and R8
Another way to decrease DEX file size is using Dynamic Delivery. Through Dynamic Delivery, users can later download and install application components on-demand. For example, account onboarding or A/B testing feature is not a feature that every user uses. We can include onboarding, A/B testing feature as an on-demand feature, and your base APK file does not contain it, which will make your DEX file smaller. See a link bellow
Use Strict Mode for detecting heavy task
Strict Mode is a powerful tool for detecting heavy tasks on the application main thread. It not only improve your app startup but also avoid frozen frame. You can set it up like this (on debug build)
When a disk read, disk write, slow call, or network on the main thread occurred, your application will show a dialog like this
Or when activity leaked, cleartext network was detected,… your app will be crashed.
Use Android Vitals and Firebase Test Lab for monitoring app performance
Android Vitals and Firebase Test Lab are useful tools for monitoring app startup time, performance, crash, ANR … You should give attention to Android Vitals after releasing your app and use Firebase Test Lab before releasing. It may help you fix the performance issues early