Making Android applications Null Pointer and Exception free

I am an android developer for iHeartRadio, and my job here is building a beautiful android application for iHeartRadio, which serves millions of android users.

Maintaining a 4.5 star rating is very, very challenging—to do so, your application must have rich contents that users can enjoy, be easy to use, and performant.

As a developer, one of my main focuses is building crash free apps. On a daily basis, I monitor and fix various kinds of application crashes, which can come from hardware limitations as we deal with multiple manufacturers, OS platform level, or developer’s own mistakes. One common class of error causes null pointer exceptions, which is familiar to any experienced java developer.

We spend a lot of effort writing clean code, expanding unit test coverage and performing peer code reviews. Still, it’s not easy to eliminate all null pointer exceptions, as the causes for them are many. They can happen during runtime when the platform API returns a null object, any server response may result in null pointer error, or the developer may simply forget to do null object check in their own code.

You may have written this to avoid null pointer exceptions. So have we!

Screen Shot 2015-08-18 at 11.38.14 AM

The above code is very defensive and prevents null pointer error.

However, the code looks ugly due to nested checks, boilerplate code, and general lack of readability.

To improve upon this, we have introduced Maybe class in our. This is similar to google guava or Java 8 java.util.Optional. Currently, android is Java 7 based, therefore we are not able to use Java 8 optional yet, and since we have our own little functional java module to support our app, we felt we can create our own optional class there so we can have our own methods that we need, rather than adopting whole Google guava library.

Here’s what the API and its implementations look like.

Screen Shot 2015-08-18 at 11.38.54 AM

Screen Shot 2015-08-18 at 11.39.06 AM

Using Maybe<T> can force a check to see if the object instance is valid before return value, and you can also put defaultValue in the same line so you don’t have to have the ugliness of having the null check in multiple places.

And here’s example of use.

Screen Shot 2015-08-18 at 11.39.17 AM

While Maybe<T> class can offer protection of unintended null pointer exceptions, and better readability in terms of possible null object instance being return, there’s still a problem in that we can’t apply everything to be Maybe<T>.

We still wanted to see how we can achieve completely null pointer exception free code in our app, so we decided to give the infer project a try (http://fbinfer.com/). Fbinfer is an open source code project from Facebook, and can analyze the whole application logic flow, detect null pointer exception errors and memory leaks in application. When I read what the project is capable of, I was thrilled, and I couldn’t wait to apply our android app. It was very easy to set up for MAC (need python 2.7 (https://www.python.org/downloads/), and opam (https://opam.ocaml.org/doc/Install.html#OSX) ) and it took about 8 minutes to analyze for a hundred thousand source lines of code without blank and comment lines which to me was reasonable.

Here’s the result.

Starting analysis (Infer version git-41fe321582e4f5d68754722eaa2284d35c25ffcc)

Analysis done

…/iheartradio/wear/shared/connection/WearConnectionManager.java:232: error: NULL_DEREFERENCE

object actualSubscribers last assigned on line 225 could be null and is dereferenced at line 232

…/iheartradio/wear/shared/connection/WearConnectionManager.java:343: error: NULL_DEREFERENCE

object listeners last assigned on line 342 could be null and is dereferenced at line 343

…/iheartradio/wear/WearRemote.java:177: error: NULL_DEREFERENCE

object data last assigned on line 176 could be null and is dereferenced at line 177

more…

So, I went to code where infer pointed out the first error

…/iheartradio/wear/shared/connection/WearConnectionManager.java:232:

Screen Shot 2015-08-18 at 11.39.47 AM

So here, infer was correct. If a developer uses the unsubscribe (final String path, final MessageListener listener) where path doesn’t exist in the map, then it will throw null pointer exception in the highlighted line.

Therefore, I added a simple check here to prevent either developer’s mistake or unintended unsubscribe method with the same path being called more than once before subscribe the path.


Screen Shot 2015-08-18 at 11.40.14 AM

Ran infer again,

Starting analysis (Infer version git-41fe321582e4f5d68754722eaa2284d35c25ffcc)

Analysis done

1214 files analyzed

…/iheartradio/wear/shared/connection/WearConnectionManager.java:347: error: NULL_DEREFERENCE

object listeners last assigned on line 346 could be null and is dereferenced at line 347

…/iheartradio/wear/WearRemote.java:177: error: NULL_DEREFERENCE

object data last assigned on line 176 could be null and is dereferenced at line 177

more…

Notice that I was able to eliminate the first one with the code change.

Nice work, infer!

My thoughts on infer were impressive, I hope that the next release they will have faster analysis time so we can run this on every pull request we have, and become a part of our tests. Next step for our application with infer would be integrating it to our build server so that we can monitor our application null pointer exception flow closely. The infer error detection doesn’t mean our users are actually facing the error, but in the name of clean code we should eliminate all possible scenarios infer indicates so that we can keep our codebase in a healthy state.