32

I tried to create an SQLite database and do some stuff with it. But I found that my onCreate method is not even invoked!!

I am sending a message to LogCat on the begining of the onCreate method.

My assumption is, the (super) constructor will invoke onCreate method. Is that right?

My Code:

import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteDatabase;
import android.content.Context;
import android.database.Cursor;
import android.content.ContentValues;
import android.util.Log;

public class DatabaseHandler extends SQLiteOpenHelper {
    // Static Constants
    /*** Database details ***/
    // Database version
    private static final int DATABASE_VERSION           = 1;

    // Database name
    private static final String DATABASE_NAME           = "database_name";

    /*** Database Tables ***/
    /** Events **/
    // Event table
    private static final String TABLE_EVENT             = "event";

    // Event table columns
    private static final String COLUMN_EVENT_EID        = "_eid";

    private static final String COLUMN_EVENT_CREATION_DATE  = "creation_date";

    private static final String COLUMN_EVENT_TITLE      = "title";
    private static final String COLUMN_EVENT_ICON       = "icon";

    public DatabaseHandler(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        Log.e("MyApp", "onCreate invoked");
        // Tables creation queries
        String CREATE_EVENT_TABLE = "create table " + TABLE_EVENT + "(" + COLUMN_EVENT_EID + " integer primary key, "
                + COLUMN_EVENT_CREATION_DATE + " text, "
                + COLUMN_EVENT_TITLE + " text, "
                + COLUMN_EVENT_ICON + " text)";

        // Creating tables
        db.execSQL(CREATE_EVENT_TABLE);
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
        Log.e("MyApp", "onUpgrade invoked");
        db.execSQL("DROP TABLE IF EXISTS " + TABLE_EVENT);
    }
}

MainActivity Code:

import android.os.Bundle;
import android.app.Activity;
import android.view.Menu;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        DatabaseHandler db = new DatabaseHandler(this);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

}
Vasily Kabunov
  • 6,511
  • 13
  • 49
  • 53
malhobayyeb
  • 2,725
  • 11
  • 57
  • 91

4 Answers4

36

The documentation says:

The database is not actually created or opened until one of getWritableDatabase() or getReadableDatabase() is called.

CL.
  • 173,858
  • 17
  • 217
  • 259
6

Let me clear the on the logic flow. Here is Lazy-initialization concept.

The (super) constructor on DatabaseHandler will not invoke onCreate method. Calling DatabaseHandler constructor will initializes: context, database name, factory that creates the database, database version, and database error handler.

getWritableDatabase() > getDatabaseLocked() > - SQLiteDatabase.create()

OR

getReadableDatabase() > getDatabaseLocked() > - SQLiteDatabase.create()

Answer: After your database gets created successfully, your configurations changes, next time again getReadableDatabase() or getWritableDatabase() calls getDatabaseLocked() and there onCreate(db) method inside getDatabaseLocked() gets executed.

enter image description here

Explanation:

The above SQLiteDatabase.create() method is responsible to create SQLiteDatabase in the disk.

But the process in lazy-initialization (mean, it doesn't make everything ready. It creates those objects on the runtime if you need them. For this it used a lot of if..else statements).

If you see the full body of getDatabaseLocked(), this is below. [You can search onCreate() method inside the body of getDatabaseLocked()]

private SQLiteDatabase getDatabaseLocked(boolean writable) {
        if (mDatabase != null) {
            if (!mDatabase.isOpen()) {
                // Darn!  The user closed the database by calling mDatabase.close().
                mDatabase = null;
            } else if (!writable || !mDatabase.isReadOnly()) {
                // The database is already open for business.
                return mDatabase;
            }
        }

        if (mIsInitializing) {
            throw new IllegalStateException("getDatabase called recursively");
        }

        SQLiteDatabase db = mDatabase;
        try {
            mIsInitializing = true;

            if (db != null) {
                if (writable && db.isReadOnly()) {
                    db.reopenReadWrite();
                }
            } else if (mName == null) {
                db = SQLiteDatabase.create(null);
            } else {
                try {
                    if (DEBUG_STRICT_READONLY && !writable) {
                        final String path = mContext.getDatabasePath(mName).getPath();
                        db = SQLiteDatabase.openDatabase(path, mFactory,
                                SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                    } else {
                        db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
                                Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
                                mFactory, mErrorHandler);
                    }
                } catch (SQLiteException ex) {
                    if (writable) {
                        throw ex;
                    }
                    Log.e(TAG, "Couldn't open " + mName
                            + " for writing (will try read-only):", ex);
                    final String path = mContext.getDatabasePath(mName).getPath();
                    db = SQLiteDatabase.openDatabase(path, mFactory,
                            SQLiteDatabase.OPEN_READONLY, mErrorHandler);
                }
            }

            onConfigure(db);

            final int version = db.getVersion();
            if (version != mNewVersion) {
                if (db.isReadOnly()) {
                    throw new SQLiteException("Can't upgrade read-only database from version " +
                            db.getVersion() + " to " + mNewVersion + ": " + mName);
                }

                db.beginTransaction();
                try {
                    if (version == 0) {
                        onCreate(db);
                    } else {
                        if (version > mNewVersion) {
                            onDowngrade(db, version, mNewVersion);
                        } else {
                            onUpgrade(db, version, mNewVersion);
                        }
                    }
                    db.setVersion(mNewVersion);
                    db.setTransactionSuccessful();
                } finally {
                    db.endTransaction();
                }
            }

            onOpen(db);

            if (db.isReadOnly()) {
                Log.w(TAG, "Opened " + mName + " in read-only mode");
            }

            mDatabase = db;
            return db;
        } finally {
            mIsInitializing = false;
            if (db != null && db != mDatabase) {
                db.close();
            }
        }
    }

Please note, inside the body of getDatabaseLocked() method, there are so many if.. else cases. These if.. else cases determines your current environment (configuration), and based on your current environment they call appropriate methods to initialize/configure whatever needed.

Also, note: All the callbacks methods in your DatabaseHandler (class that implemented SQLiteOpenHelper) are called inside the getDatabaseLocked() body.

Source code SQLiteOpenHelper.java: https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/database/sqlite/SQLiteOpenHelper.java

Source code SQLiteDatabase.java: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/database/sqlite/SQLiteDatabase.java

Sample to follow: https://github.com/uddhavgautam/SQLiteBasicSample

Uddhav P. Gautam
  • 7,362
  • 3
  • 47
  • 64
4

Your are right, the (super) constructor will invoke onCreate method, BUT only if the actual database does not exits. From http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#onCreate%28android.database.sqlite.SQLiteDatabase%29

A helper class to manage database creation and version management.

You create a subclass implementing onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int) and optionally onOpen(SQLiteDatabase), and this class takes care of opening the database if it exists, creating it if it does not, and upgrading it as necessary.

Ricardo Rivaldo
  • 1,687
  • 2
  • 15
  • 13
1

As the official documents says, "getWritableDatabase () Create and/or open a database that will be used for reading and writing. The first time this is called, the database will be opened and onCreate(SQLiteDatabase), onUpgrade(SQLiteDatabase, int, int) and/or onOpen(SQLiteDatabase) will be called."

Once opened successfully, the database is cached, so you can call this method every time you need to write to the database. (Make sure to call close() when you no longer need the database.) Errors such as bad permissions or a full disk may cause this method to fail, but future attempts may succeed if the problem is fixed.

http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#getWritableDatabase()

Sam003
  • 540
  • 6
  • 17