Ответ 1
Проблема исправлена, начиная с уровня API 11. Теперь существует интерфейс: DatabaseErrorHandler, который можно реализовать, чтобы определить свой собственный метод onCorruption(). При открытии базы данных вы можете передать этот DatabaseErrorHandler в качестве параметра конструктору SQLiteOpenHelper.
например.
public class MyDbErrorHandler implements DatabaseErrorHandler {
@Override
onCorruption(SQLiteDatabase db) {
// Back up the db or do some other stuff
}
}
SQLiteOpenHelper dbHelper = new SQLiteOpenHelper(context, "MyDbName", null, 1,
new MyDbErrorHandler());
SQLiteDatabase db = dbHelper.getWritableDatabase();
Для систем с уровнем API ниже 11 и для тех, кто не хочет использовать этот подход, существует несколько альтернатив.
1. Резервное копирование данных Android
Android предлагает услугу резервного копирования, которая автоматически копирует данные приложения в удаленное облачное хранилище. Если база данных повреждена или приложение переустановлено после factory reset. Данные приложения могут быть восстановлены из удаленных данных.
Для получения дополнительной информации см.: http://developer.android.com/guide/topics/data/backup.html
2. JDBC (sqldroid)
Одним из подходов может быть использование собственного соединителя базы данных, либо собственного JDBC, либо библиотеки sqldroid. Он официально не поддерживается google, и вы не можете быть уверены, будет ли он доступен в будущих версиях Android.
3. Berkley DB Java Edition
Интересный подход, а также взгляд на производительность обработки больших объемов данных - это Berkley DB Java Edition.
Вот учебник, как использовать его в Android: http://download.oracle.com/docs/cd/E17277_02/html/HOWTO-Android.html
4. Настройка библиотек android
Еще один рискованный подход - реализовать свой собственный класс базы данных путем копирования или расширения SQLiteDatabase.java из источника android и переопределения или переопределения критических частей, которые:
public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
SQLiteDatabase sqliteDatabase = null;
try {
// Open the database.
sqliteDatabase = new SQLiteDatabase(path, factory, flags);
if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
sqliteDatabase.enableSqlTracing(path);
}
if (SQLiteDebug.DEBUG_SQL_TIME) {
sqliteDatabase.enableSqlProfiling(path);
}
} catch (SQLiteDatabaseCorruptException e) {
// Try to recover from this, if we can.
// TODO: should we do this for other open failures?
Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
EventLog.writeEvent(EVENT_DB_CORRUPT, path);
if (!path.equalsIgnoreCase(":memory")) {
// delete is only for non-memory database files
new File(path).delete();
}
sqliteDatabase = new SQLiteDatabase(path, factory, flags);
}
ActiveDatabases.getInstance().mActiveDatabases.add(
new WeakReference<SQLiteDatabase>(sqliteDatabase));
return sqliteDatabase;
}
и
/* package */ void onCorruption() {
Log.e(TAG, "Removing corrupt database: " + mPath);
EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
try {
// Close the database (if we can), which will cause subsequent operations to fail.
close();
} finally {
// Delete the corrupt file. Don't re-create it now -- that would just confuse people
// -- but the next time someone tries to open it, they can set it up from scratch.
if (!mPath.equalsIgnoreCase(":memory")) {
// delete is only for non-memory database files
new File(mPath).delete();
}
}
}
Опасной частью этого является то, что вам также придется переопределить вспомогательные классы, которые обращаются к SQLiteDatabase, например SQLiteOpenHelper
. Поскольку класс SQLiteDatabase
использует методы factory, вы можете столкнуться с неожиданными побочными эффектами.