Search code examples
androidsingletonmvpsqliteopenhelper

Handling SQLite singleton instances and its Context dependency in Android


I am currently working on an Android project that performs a lot of communication with the SQLite database. I am also trying to implement a MVP framework within the app.

My current implementation of the Singleton instance is similar to the following. (taken from this post: https://github.com/codepath/android_guides/wiki/Local-Databases-with-SQLiteOpenHelper )

public class PostsDatabaseHelper extends SQLiteOpenHelper { 
  private static PostsDatabaseHelper sInstance;

  public static synchronized PostsDatabaseHelper getInstance(Context context) {
    if (sInstance == null) {
      sInstance = new PostsDatabaseHelper(context.getApplicationContext());
    }
    return sInstance;
  }

  private PostsDatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
  }
}

With the existing code above, I call the getInstance method in several Presenter classes, passing into each of them the Context object which was passed on from the Activity/Fragment. The Context objects can be passed across multiple classes.

Instead of the code above, I was thinking of instantiating the databaseHelper only once at the start of the Application, and then all references will then point to a variant of the getInstance method without the context dependency.

EDIT: My main aim here is to remove as much as possible, the presence of the Context object in the Presenter classes, so as to make the code 'cleaner'. Because all the calls to getInstance provide/inject the same type of Context (the Application's context and not an Activity-specific context), I don't see a need to put the Context object as an argument.

public class PostsDatabaseHelper extends SQLiteOpenHelper { 
  private static PostsDatabaseHelper sInstance;

  // called by all other classes
  public static synchronized PostsDatabaseHelper getInstance() {
    if (sInstance == null) {
      //throw error
    }
    return sInstance;
  }

  // only called once at the start of the Application
  public static void instantiateInstance(Context context){
    sInstance = new PostsDatabaseHelper(context.getApplicationContext());
  }

  private PostsDatabaseHelper(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
  }
} 

What I would like to know is, will there be any downsides to this approach? Thanks!


Solution

  • You are trading lazy initialization for static initialization.

    In general, lazy initialization can amortize the cost of initialization over the life of an application. In this case it seems less important, for two reasons:

    1. It is almost certain that you will need this DB. It seems unlikely that by putting the initialization off, you might avoid having to do it at all.
    2. The Android framework guarantees that the DBHelper constructor can be run from the UI thread: it is not what takes the time. The thing that takes the time is the first call to getWriteableDatabase. The lazy creation of the Helper accomplishes almost nothing.

    You might consider making the code even less convoluted, by initializing the DB in the Application like this:

    public class DBDrivenApp extends Application implements DBProvider {
    
      // ...
    
      private PostsDatabaseHelper db;
    
      // ...
    
      @Override
      public void onCreate() {
        super.onCreate();
        db = new PostsDatabaseHelper(this);
      }
    
      @Override
      public PostsDatabaseHelper getDB() { return db; }
    
      // ...
    }
    

    ... and, better yet, use an IoC framework, like Dagger2, to inject the database instance, so that you can mock it in testing.