Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I notify the user that SQLite Asset Helper is loading the database for the first time?

I'm using the SQLite Asset Helper library to handle the dirty work of setting up and upgrading my app's database. It works really well but unfortunately I have yet to figure out a way to notify the user when the library:

A) loads the database for the first time (by unzipping it from the \assets\databases\ folder)

or

B) upgrades the database (using information in an upgraded database file in \assets\datates)

I tried putting this code in my app's main Activity.onCreate(), thinking I could load the database (if it didn't exist) on the main thread while distracting the user with a un-dismiss-able AlertDialog:

    File dbFile=this.getDatabasePath("gShoJMDict");
    Boolean dbExists = dbFile.exists();
    Log.i("ActivityStartScreen", String.valueOf(dbExists));

    if(!dbExists)
    {
        DialogFirstRun dialogFirstRun = new DialogFirstRun();
        dialogFirstRun.show(getSupportFragmentManager(), "dialogFirstRun");
        dialogFirstRun.setCancelable(false);
        DictHelper helper = new DictHelper(this);
        helper.getReadableDatabase();
        helper.close();
        dialogFirstRun.dismiss();
    }

Unfortunately it appears (based on LogCat entries) that SQLite Asset Helper checks to see if the database exists well before onCreate(), so by the time the above chunk of code runs, the database already exists so the dialog never shows up.

I'm using a ContentProvider, and I've verified that I'm only calling getReadableDatabase() from within query() or update(). My ContentProvider's onCreate() looks like this...

@Override
public boolean onCreate()
{
    // Load our database
    gdb = new JMDictHelper(getContext());
    return true;
}

...but despite moving gdb = new JMDictHelper(getContext()); into query() or update(), SQLite Asset Helper library still loads the database well before I can notify the user.

What can I do in this situation to intercept the initial setup or upgrade of the database and notify the user that the app is busy performing these tasks? Right now the app just sits there doing nothing until the library finishes - that's fine for testing since I know to expect it, but I can't leave it like that when I'm ready for the app to go live.

like image 564
IAmKale Avatar asked May 23 '13 18:05

IAmKale


People also ask

Which method is called only once when the database is created for the first time?

Methods of SQLiteOpenHelper class called only once when database is created for the first time.

Which method gets called when the SQL Light database is created for the first time this is where creation of table should happen?

onCreate(SQLiteDatabase database) – is the method which is called first time when the database is created and we need to use this method to create the tables and populate it as per the need. onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) – is the method called when upgrade is done.

Which method is called when SQLite database needs to be upgraded?

In our onUpgrade, we defined the following: @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log. e(TAG, "Updating table from " + oldVersion + " to " + newVersion); //Added new column to book table - book rating if (oldVersion < 2){ db. execSQL(DROP + BookEntry.

What is the use of SQLite open helper class in SQLite?

We can use this class for creating a database and also we can use it for version management. This class provides the onCreate() and onUpgrade() methods for performing any database operation. SQLiteOpenHelper class has two constructors. SQLiteOpenHelper(Context context, String name, SQLiteDatabase.


2 Answers

Just in case you're still wondering about this or anyone else has this question, here's a solution to a similar problem that I just implemented. SQLiteAssetHelper doesn't copy the database until you call getReadableDatabase() or getWritableDatabase(). If one of those methods is being called before MainActivity.onCreate(), it might be because you're calling it in a ContentProvider's onCreate() method. Here's the method description from the documentation:

Implement this to initialize your content provider on startup. This method is called for all registered content providers on the application main thread at application launch time. It must not perform lengthy operations, or application startup will be delayed.

You should defer nontrivial initialization (such as opening, upgrading, and scanning databases) until the content provider is used (via query(Uri, String[], String, String[], String), insert(Uri, ContentValues), etc). Deferred initialization keeps application startup fast, avoids unnecessary work if the provider turns out not to be needed, and stops database errors (such as a full disk) from halting application launch.

If you use SQLite, SQLiteOpenHelper is a helpful utility class that makes it easy to manage databases, and will automatically defer opening until first use. If you do use SQLiteOpenHelper, make sure to avoid calling getReadableDatabase() or getWritableDatabase() from this method. (Instead, override onOpen(SQLiteDatabase) to initialize the database when it is first opened.)

You can make your calls to getReadableDatabase() or getWritableDatabase() when your ContentProvider is actually accessed, as described here.

Now that your ContentProvider isn't holding things up, you can check whether the database exists in MainActivity.onCreate() and, if necessary, copy it in a background thread while displaying a ProgressDialog:

// Create an AsyncTask to copy the database in a background thread while
// displaying a ProgressDialog.
private class LoadDatabaseTask extends AsyncTask<Context, Void, Void> {
    Context mContext;
    ProgressDialog mDialog;

    // Provide a constructor so we can get a Context to use to create
    // the ProgressDialog.
    LoadDatabasesTask(Context context) {
        super();
        mContext = context;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        mDialog = new ProgressDialog(mContext);
        mDialog.setMessage(mContext.getString("Loading database..."));
        mDialog.show();
    }

    @Override
    protected Void doInBackground(Context... contexts) {
        // Copy database.
        new MyAssetHelper(contexts[0]).getReadableDatabase();
        return null;
    }

    @Override
    protected void onPostExecute(Void result) {
        super.onPostExecute(result);
        mDialog.dismiss();
    }
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...

    // Install databases if necessary.
    File database = getDatabasePath(DB_NAME);
    if (!database.exists()) {
        new LoadDatabaseTask(this).execute(this, null, null);
    }

    // ...
}
like image 155
Ben Avatar answered Sep 29 '22 23:09

Ben


Source code tells, that AssetHelper correctly inherits from SqliteOpenHelper. Without any of your code or logcat to look into, the only sane place where your database can be created before onCreate of main Activity is some class static initialization (if you are not using Application object). Check for that.

As for notification: use EmptyView placeholder when data is not ready and use Loaders, because:

  1. they do not block user interface: user can see in EmptyView that the data is processed and even can leave activity for a while.

  2. they do not die on the destruction of an Activity and can be reused.

  3. they are Standard.

like image 45
alexei burmistrov Avatar answered Sep 30 '22 01:09

alexei burmistrov