(So far we’ve analyzed a lot of apps and discovered a handful of issues that significantly slow down many apps. Starting from this post, we’ll describe these issues one by one.)
Reflection is, of course, an extremely useful aspect of Java and Android development. Yet it turns out that reflection can very often be the source of significant slowdown within an Android application. Perhaps the most intuitive way of understanding this is going through a couple of real-life examples.
Our first example involves NYTimes Android app. With the help of NimbleDroid, our friends at NYTimes found out that the reflective type adapters in Gson cost their Android app a 700ms startup delay. They eventually fixed this delay with manually written custom type adapters.
Our second example involves Photobucket, a large photo-sharing platform. Here, reflection again causes a big bottleneck.
660ms for call com.photobucket.api.client.jersey.UserClient constructor
We see that the com.photobucket.api.client.jersey.UserClient
constructor takes an entire 660ms to run. Looking further into the icicle graph, we see that the reason for such a lag lies in reflection. Check this out:
lots of reflection calls, like: java.lang.Class.getGenericInterfaces
Note that the getGenericInterfaces() method returns the types of the interfaces that this class directly implements. Here, it is invoked 5 times and takes ~81ms. Sure, on the surface this may not seem like much, but altogether, use of the method is causing ~600ms of start time delay. Let’s take a deeper look at why this is taking so long.
NEXUS 5 (6.0) ART | GALAXY S5 (5.0) ART | GALAXY S3 mini (4.1.2) Dalvik | |
---|---|---|---|
getFields | 1108 | 1626 | 27083 |
getDeclaredFields | 347 | 951 | 7687 |
getGenericInterfaces | 16 | 23 | 2927 |
getGenericSuperclass | 247 | 298 | 665 |
makeAccessible | 14 | 147 | 449 |
getObject | 21 | 167 | 127 |
setObject | 21 | 201 | 161 |
createDummyItems | 312 | 358 | 774 |
createDummyItemsWithReflection | 1332 | 6384 | 2891 |
It’s evident that reflection in Android is excruciatingly slow - compare the (1332ms, 6384ms, 2891ms) with reflection to the (312ms, 358ms, 774ms) without reflection. Interestingly, Android 5.0 ART on a more powerful device actually makes reflection much slower than Android 4.1 Dalvik on a less powerful device; only in Android 6.0 ART is the overhead reduced, but the overhead is still quite significant.
ActiveAndroid is another library that uses reflection. Let’s take a look at how it can affect start time by analyzing some real apps on the Play store:
Here’s the Scribd app:
1093ms for call com.activeandroid.ActiveAndroid.initialize
Myntra sees similar problems:
1421ms for call com.activeandroid.ActiveAndroid.initialize
As you can see, the library requires more than a second to initialize. That’s a lot of time, especially taking into consideration that users expect an average app start time of 2s.
To conclude, reflection in Android is really slow. To guarantee you offer users the smoothest experience possible, we recommend the following:
Recommendation: avoid using reflection (or libraries that use reflection) altogether. In particular, do not use reflective type adapters to serialize Java objects.