views:

1609

answers:

2

I've had fine luck using SQLite with straight, direct SQL in Android, but this is the first time I'm wrapping a DB in a ContentProvider. I keep getting a null pointer exception when calling getWritableDatabase() or getReadableDatabase(). Is this just a stupid mistake I've made with initializations in my code or is there a bigger issue?

public class DatabaseProvider extends ContentProvider {
  ...
  private DatabaseHelper                   databaseHelper;
  private SQLiteDatabase                   db;
  ...
  @Override
  public boolean onCreate() {
    databaseHelper = new DatabaseProvider.DatabaseHelper(getContext());
    return (databaseHelper == null) ? false : true;
  }
  ...
  @Override
  public Uri insert(Uri uri, ContentValues values) {   
    db = databaseHelper.getWritableDatabase(); // NULL POINTER EXCEPTION HERE
    ...
  }
  private static class DatabaseHelper extends SQLiteOpenHelper {
    public static final String DATABASE_NAME = "cogsurv.db";
    public static final int DATABASE_VERSION = 1;

    public static final String[] TABLES = {
      "people", 
      "travel_logs", 
      "travel_fixes",
      "landmarks", 
      "landmark_visits",
      "direction_distance_estimates" 
    };

    // people._id does not AUTOINCREMENT, because it's set based on server's people.id
    public static final String[] CREATE_TABLE_SQL = {
      "CREATE TABLE people (_id INTEGER PRIMARY KEY," + 
                 "server_id INTEGER," +
                 "name VARCHAR(255)," +
                 "email_address VARCHAR(255))",
      "CREATE TABLE travel_logs (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
                                "server_id INTEGER," +
                                "person_local_id INTEGER," +
                                "person_server_id INTEGER," +
                                "start DATE," +
                                "stop DATE," +
                                "type VARCHAR(15)," +
                                "uploaded VARCHAR(1))",
      "CREATE TABLE travel_fixes (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
                                 "datetime DATE, " +
                                 "latitude DOUBLE, " +
                                 "longitude DOUBLE, " +
                                 "altitude DOUBLE," +
                                 "speed DOUBLE," +
                                 "accuracy DOUBLE," +
                                 "travel_mode VARCHAR(50), " +
                                 "person_local_id INTEGER," +
                                 "person_server_id INTEGER," +
                                 "travel_log_local_id INTEGER," +
                                 "travel_log_server_id INTEGER," +
                                 "uploaded VARCHAR(1))",
      "CREATE TABLE landmarks (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
                              "server_id INTEGER," +
                              "name VARCHAR(150)," +
                              "latitude DOUBLE," +
                              "longitude DOUBLE," +
                              "person_local_id INTEGER," +
                              "person_server_id INTEGER," +
                              "uploaded VARCHAR(1))",
      "CREATE TABLE landmark_visits (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
                                    "server_id INTEGER," +
                                    "person_local_id INTEGER," +
                                    "person_server_id INTEGER," +
                                    "landmark_local_id INTEGER," +
                                    "landmark_server_id INTEGER," +
                                    "travel_log_local_id INTEGER," +
                                    "travel_log_server_id INTEGER," +
                                    "datetime DATE," +
                                    "number_of_questions_asked INTEGER," +
                                    "uploaded VARCHAR(1))",
      "CREATE TABLE direction_distance_estimates (_id INTEGER PRIMARY KEY AUTOINCREMENT," +
                                                 "server_id INTEGER," +
                                                 "person_local_id INTEGER," +
                                                 "person_server_id INTEGER," +
                                                 "travel_log_local_id INTEGER," +
                                                 "travel_log_server_id INTEGER," +
                                                 "landmark_visit_local_id INTEGER," +
                                                 "landmark_visit_server_id INTEGER," +
                                                 "start_landmark_local_id INTEGER," +
                                                 "start_landmark_server_id INTEGER," +
                                                 "target_landmark_local_id INTEGER," +
                                                 "target_landmark_server_id INTEGER," +
                                                 "datetime DATE," +
                                                 "direction_estimate DOUBLE," +
                                                 "distance_estimate DOUBLE," +
                                                 "uploaded VARCHAR(1))"
    };

    public DatabaseHelper(Context context) {
      super(context, DATABASE_NAME, null, DATABASE_VERSION);
      Log.v(Constants.TAG, "DatabaseHelper()");
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
      Log.v(Constants.TAG, "DatabaseHelper.onCreate() starting");

      // create the tables
      int length = CREATE_TABLE_SQL.length;
      for (int i = 0; i < length; i++) {
        db.execSQL(CREATE_TABLE_SQL[i]);
      }
      Log.v(Constants.TAG, "DatabaseHelper.onCreate() finished");
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
      for (String tableName : TABLES) {
        db.execSQL("DROP TABLE IF EXISTS" + tableName);
      }
      onCreate(db);
    }
  }
}

As always, thanks for the assistance!

--

Not sure if this detail helps, but here's LogCat showing the exception: alt text

+1  A: 

Rather than:

databaseHelper = new DatabaseProvider.DatabaseHelper(getContext());

try:

databaseHelper = new DatabaseProvider.DatabaseHelper(this);

and see if that helps.

If it does not, could you post the NullPointerException stack trace?

CommonsWare
Thanks for the response! Unfortunately that doesn't help. "this" is a ContentProvider rather than a Context.I've tried posting a screenshot of the error. It's wide, so you'll want to open it up in a new tab. Not sure if that detail really helps, though...
Drew Dara-Abrams
Oops, you are correct -- I keep thinking ContentProvider inherits from Context, and it doesn't. With respect to the stack trace, it isn't loading for me. You can use DDMS (or the DDMS perspective in Eclipse) to save a portion of a stack trace as a text file. Just highlight the lines of interest and click the Save icon in the toolbar of the LogCat tab.
CommonsWare
Thanks for the offer of more help! Just the other day, I sat down with an Android pro, and he was able to fix my problem: do the getWritableDatabase() as part of the onCreate() rather than in the insert()
Drew Dara-Abrams
Hmm, actually now that I've gotten around to trying to use the DB in the insert() method, I'm getting the same null pointer exception.Time to ask some people at Google about this, given that I've been getting e-mail from others wondering about my "solution."
Drew Dara-Abrams
Are you using `ContentValues` in your `insert()` method?
Anthony Forloney
Yes. Is that a potential problem?
Drew Dara-Abrams
A: 

Make sure when you're creating the AVD, specify some amount of SD card space. I had the same problem and this solved it. If this doesn't help, you can also try to start the emulator with -wipe-data option.

jaxvy
Thanks for the suggestion, but I always test on an actual device, so I don't think this is the issue.
Drew Dara-Abrams
What about the -wipe-data option? Maybe this might help to make it work.
jaxvy