Monday, July 25, 2011

Getting strict in an existing Honeycomb application

During the last few months I have been involved in the development of a tablet based application targeting Honeycomb that does pretty heavy use of network and database access. We have been relying on a set of acceptance tests to evaluate its performance, but taking giant leaps of faith is nothing to be proud of. So... people who love to make sure that they are doing things the right way: welcome StrictMode!

What is StrictMode?
It's a developer tool introduced in Gingerbread that is mostly used to detect lengthy operations running on your application's main thread, such as disk reads/writes and network access. It can also catch leaked SQLite cursors and resources that have not been properly closed. You can display an annoying dialog when a violation happens, log it or just crash the entire application. There are several articles describing StrictMode in detail, so go read them first.

The configuration used was the proposed in the reference documentation:
public void onCreate() {
     if (DEVELOPER_MODE) {
      StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
       .detectDiskReads()
       .detectDiskWrites()
       .detectNetwork()
       .penaltyLog()
       .build());
      
      StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
       .detectLeakedSqlLiteObjects()
       .detectLeakedClosableObjects()
       .penaltyLog()
       .penaltyDeath()
       .build());
     }
     
     
     super.onCreate();
}

What did we find?
  • Disk access
Each time a shared preference was read or written from the UI thread a violation got logged.  I don't feel the need to move this to a separate thread, so I've just ignored these violations.

Calls to getReadableDatabase and getWritableDatabase were also considered to be violations. The documentation of SQLiteOpenHelper states that both these methods should not be called from the application main thread, so this was something new.

There were a lot of image files accidentally being read from the filesystem within the main thread. The fix was simple and the impact in performance was huge.
  • Leaked cursors
The database usage is somewhat extensive and there is a lot of code dealing with cursors. Of course we've screwed it in some places. StrictMode showed several unclosed cursors across the application.
  • Network access
There were no violations related to network access. Hooray for the team!
  • Other issues found unrelated to responsiveness
Most of the application fragments don't have a default constructor. This hasn't been an issue until now, but enabling StrictMode started throwing a lot of instantiation exceptions. The application doesn't run on portrait mode for now, but enabling orientation changes did also throw this type of exception. Hence, there seems to be Android code relying on fragments having a default constructor. The solution was to provide a default constructor and handle instantiation parameters from factory methods as shown in the fragments documentation.

Conclusion
StrictMode is an invaluable tool that should be enabled from the beginning of every project. We are currently enabling it in all our ongoing projects and it's helping to make us feel a little less stupid every time we catch something right on the first launch.