I am working on speeding up the app launches. I am seeing that the very first creation of a ~1400 entries map of DispatchingAndroidInjector takes more than 2 seconds. My theory is that each entry in the map results in the corresponding class load and that results in dex file IO. Can anyone confirm the reason of this slowness? I am looking to reduce this latency and trying to determine if reducing the map size will speed up the cold starts.
DispatchingAndroidInjector(
Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys,
Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys) {
this.injectorFactories = merge(injectorFactoriesWithClassKeys, injectorFactoriesWithStringKeys);
}```
Initial classloading for dagger.android
Map population is known to be an expensive startup cost, and experimentalUseStringKeys
(introduced in Dagger 2.17 in August 2018) was meant to fix it. See Ron Shapiro's comments in issue 1064.
From the commit:
Add an option to use string keys for dagger.android and allow the keys to be obfuscated
This is enabled by default internally, where proguard understands
-identifiernamestring
, but disabled externally where it doesn't (and where R8 is not yet the standard). It can be enabled with the-Adagger.android.experimentalUseStringKeys
flag for users that don't use proguard. This way we can guarantee that any project without modification will continue to work when proguarded.This solves an issue where classloading of every Activity/Fragment/Service injector that's a child of the root component is forced into application startup. [...]
One of the hazards of this solution is that class names can be obfuscated via Proguard/R8/AppReduce, so the most built-in workaround requires a version of R8 that supports -identifiernamestring
as in the AndroidProcessor documentation linked above. (R8 was new at the time that this feature came out, but after five years the availability and stability of R8 have increased significantly.)
Other solutions:
-keepnames
.Map<Class, AndroidInjector>
in the background.Though it is possible to work around this by keeping Class keys but adding a qualifier annotation onto your @Binds @IntoMap
binding, you would be shutting yourself out of using @ContributesAndroidInjector
and the default AndroidInjection/AndroidSupportInjection classes. If the other solutions above don't work, and you would find it necessary to refactor all 1400 (!) of your bindings to support this manual binding workaround, you might find it easier just to pursue a migration to Hilt instead.