Wednesday, May 30, 2012

Basic Android Debugging with Logs

As with most areas in software engineering, debugging is a crucial aspect of Android development. Properly setting up your application for debugging can save you hours of work and frustration. Unfortunately, in my experience not many beginners learn how to properly make use of the utility classes provided in the Android SDK. Unless you are an experienced developer, it is my personal belief that Android debugging should follow a pattern. This will prove beneficial for a couple reasons:

  • It allows you to anticipate bugs down the line. Setting up your development work space for debugging will give you a head start on bugs you might encounter in the future.

  • It gives you centralized control over the debugging process. Disorganized and sparse placement of log messages in your class can clutter your logcat output, making it difficult to interpret debugging results. The ability to toggle certain groups of log messages on/off can make your life a whole lot easier, especially if your application is complex.

The Log Class

For those of you who don't know, the Android SDK includes a useful logging utility class called android.util.Log. The class allows you to log messages categorized based severity; each type of logging message has its own message. Here is a listing of the message types, and their respective method calls, ordered from lowest to highest priority:

  • The Log.v() method is used to log verbose messages.
  • The Log.d() method is used to log debug messages.
  • The Log.i() method is used to log informational messages.
  • The Log.w() method is used to log warnings.
  • The Log.e() method is used to log errors.
  • The Log.wtf() method is used to log events that should never happen ("wtf" being an abbreviation for "What a Terrible Failure", of course). You can think of this method as the equivalent of Java's assert method.

Saturday, May 26, 2012

Reaping the Benefits of the LoaderManager Class

Note: Check out my new posts on Loaders here! (Since writing this post, I've written extensively on Loaders and the LoaderManager).

With Android 3.0 came the introduction of the LoaderManager class, an abstract class associated with an Activity or Fragment for managing one or more Loader instances. The LoaderManager class is one of my favorite additions to the Android framework for a number of reasons, but mostly because it significantly reduces code complexity and makes your application run a lot smoother. Implementing data loaders with the LoaderManager is simple to implement, and takes care of everything about lifecycle management so are much less error prone.

While applications are free to write their own loaders for loading various types of data, the most common (and simplest) use of the LoaderManager is with a CursorLoader. When done correctly, the CursorLoader class offloads the work of loading data on a thread, and keeps the data persistent during short term activity refresh events, such as an orientation change. In addition to performing the initial query, the CursorLoader registers a ContentObserver with the dataset you requested and calls forceLoad() on itself when the data set changes, and is thus auto-updating. This is extremely convenient for the programmer, as he doesn't have to worry about performing these queries himself. Further, for bigger screens it becomes more important that you query on a separate thread since configuration changes involve recreating the entire view layout, a complex operation that can cause disasters when blocked.

Thursday, May 24, 2012

Using "newInstance()" to Instantiate a Fragment

I recently came across an interesting question on StackOverflow regarding Fragment instantiation:

What is the difference between new MyFragment() and MyFragment.newInstance()? Should I prefer one over the other?

Good question. The answer, as the title of this blog suggests, is a matter of proper design.

Here, the newInstance() method is what we call a "static factory method", a simple pattern that is often used as a way to instantiate an object without directly calling the object's default constructor. For example, it is often used to implement a Singleton design pattern:

public static class Singleton {

    private static final Singleton instance = null;
    
    /** 
     * Make the class private to prevent direct instantiation. This 
     * forces clients to call newInstance(), which will ensure the
     * class' Singleton property.
     */
    private Singleton() { }
   
    /**
     * If instance is null, then instantiate the object by calling
     * the default constructor (this is OK since we are calling
     * it from within the class). This method should be marked
     * "synchronized" if you plan on calling it from multiple threads!
     */ 
    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

Monday, May 21, 2012

Correctly Managing Your SQLite Database

One thing that I've noticed other Android developers having trouble with is properly setting up their SQLiteDatabase. Often times, I come across questions on StackOverflow asking about error messages such as,

E/Database(234): Leak found
E/Database(234): Caused by: java.lang.IllegalStateException:
                 SQLiteDatabase created and never closed

As you have probably figured out, this exception is thrown when you have opened more SQLiteDatabase instances than you have closed. Managing the database can be complicated when first starting out with Android development, especially to those who are just beginning to understand the Activity lifecycle. The easiest solution is to make your database instance a singleton instance across the entire application's lifecycle. This will ensure that no leaks occur, and will make your life a lot easier since it eliminates the possibility of forgetting to close your database as you code.

Here are two examples that illustrates three possible approaches in managing your singleton database. These will ensure safe access to the database throughout the application.