Android framework classes, choose wisely!
A.K.A. Always read the documentation for Classes you use.
For most of my Android life I’ve had a Nexus device so that I could always easily flash different versions of Android to suit my needs. Knowing that I can easily jump to the latest and greatest as soon as it’s released using Googles Nexus factory images is extremely reassuring and useful as a developer.
Sometimes being on the latest and greatest means you are at the forefront of bugs in both the system and on applications. One such bug I encountered when upgrading to Android Marshmallow (6.0) was with the official Tumblr app which would crash almost any time I opened the Notifications tab (seen below).
Checking Logcat shortly after one of these crashes revealed the following piece of information:
java.lang.AbstractMethodError: abstract method “int android.text.ParcelableSpan.getSpanTypeIdInternal()”
Odd! Normally IDE’s and compilers will pick up on these kind of errors and inform the developer before letting them continue. Why was this one any different?
As you can see in the image below the class Test does not cause any errors to be shown in the IDE (in this case the IDE is Android Studio which is based on IntelliJ).
If you were to use the Test class in an app running on Android 6.0 or above you’d encounter the AbstractMethodError exception detailed above because you aren’t in fact implementing all of the methods required by the ParcelableSpan interface. If you look at the source of ParcelableSpan in the Marshmallow source code you will notice two extra methods that were added in Marshmallow; getSpanTypeIdInternal and writeToParcelInternal (shown below).
You’ll notice that the methods have the Android @hide Javadoc annotation which means that not only are these methods hidden from exported Javadoc but also from implementing classes. This is why you likely won’t receive a warning in your IDE.
This is why it’s always worth reading the documentation of a class to make sure you know and understand it’s expected use case. But I thought that the @hide annotation removed traces of the methods that were annotated with it? It does, however in the case of ParcelableSpan the changes seem to have been made solely because the interface itself shouldn’t have been used by applications like Tumblr in the first place as per the documentation shown below.
As a proof of concept I proved that the issue with Tumblr was caused by the lack of these methods by decompiling version 220.127.116.11 of the Tumblr app, adding the two missing methods, recompiling and running the app.
All in all be VERY careful when choosing to use a class and take note if something is explicitly labeled as framework only. Every change made to a framework class you’re not meant to be using has the potential to bring down your app and ultimately give your users a bad experience. Remember: users come first.
TL:DR — Always take a few moments to read the documentation of classes you are implementing, extending or otherwise using to make sure you are using them for their intended purpose or more importantly meant to be using them in the first place.