Ответ 1
В документации говорится:
База данных фактически не создается или не открывается до тех пор, пока одна из getWritableDatabase() или getReadableDatabase() не будет называется.
Я попытался создать базу данных SQLite и сделать с ней кое-что. Но я обнаружил, что мой метод onCreate
даже не вызывается!!
Я отправляю сообщение в LogCat в начале метода onCreate
.
Мое предположение заключается в том, что конструктор (супер) вызовет метод onCreate
. Правильно ли это?
Мой код:
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:
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;
}
}
В документации говорится:
База данных фактически не создается или не открывается до тех пор, пока одна из getWritableDatabase() или getReadableDatabase() не будет называется.
Вы правы, конструктор (супер) будет вызывать метод onCreate, НО только если фактическая база данных не выйдет. Из http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#onCreate%28android.database.sqlite.SQLiteDatabase%29
Вспомогательный класс для управления созданием базы данных и управлением версиями.
Создается подкласс, реализующий onCreate (SQLiteDatabase), onUpgrade (SQLiteDatabase, int, int) и необязательно onOpen (SQLiteDatabase), и этот класс заботится об открытии если он существует, создавая его, если он этого не делает, и обновляя его как необходимо.
Как говорится в официальных документах: getWritableDatabase() Создать и/или открыть базу данных, которая будет использоваться для чтения и записи. При первом вызове база данных будет открыта и onCreate (SQLiteDatabase), onUpgrade (SQLiteDatabase, int, int) и/или onOpen (SQLiteDatabase). "
После успешного открытия база данных кэшируется, поэтому вы можете вызывать этот метод каждый раз, когда вам нужно писать в базу данных. (Обязательно вызовите функцию close(), когда вам больше не нужна база данных.) Ошибки, такие как неправильные разрешения или полный диск, могут привести к сбою этого метода, но будущие попытки могут быть успешными, если проблема исправлена.
Позвольте мне очистить логический поток. Вот концепция Lazy-initialization.
Конструктор (супер) на DatabaseHandler
не будет вызывать метод onCreate. Вызов конструктора DatabaseHandler инициализирует: контекст, имя базы данных, factory, который создает базу данных, версию базы данных и обработчик ошибок базы данных.
getWritableDatabase() > getDatabaseLocked() > - SQLiteDatabase.create()
ИЛИ
getReadableDatabase() > getDatabaseLocked() > - SQLiteDatabase.create()
Ответ:. После успешной создания базы данных ваши конфигурации меняются, в следующий раз снова getReadableDatabase()
или getWritableDatabase()
вызывает getDatabaseLocked()
и там выполняется onCreate(db)
метод внутри getDatabaseLocked()
.
Объяснение:
Вышеупомянутый метод SQLiteDatabase.create()
отвечает за создание SQLiteDatabase на диске.
Но процесс в ленивой инициализации (в среднем, он не делает все готовым, он создает эти объекты во время выполнения, если они вам понадобятся, для этого он использовал множество операторов if..else
).
Если вы видите полное тело getDatabaseLocked()
, это ниже. [Вы можете найти метод onCreate()
внутри тела 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();
}
}
}
Обратите внимание, внутри тела метода getDatabaseLocked()
есть так много случаев if else. Эти случаи if.. else определяют вашу текущую среду (конфигурацию), и на основе вашей текущей среды они вызывают соответствующие методы для инициализации/настройки любого необходимого.
Также обратите внимание: Все методы обратных вызовов в DatabaseHandler
(класс, который реализовал SQLiteOpenHelper
) вызываются внутри тела getDatabaseLocked()
.
Исходный код SQLiteOpenHelper.java
:
https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/database/sqlite/SQLiteOpenHelper.java
Исходный код SQLiteDatabase.java
:
https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/database/sqlite/SQLiteDatabase.java