pinkertem
@pinkertem

Как выяснить причину ошибки «database disk image is malformed» в android?

Я создал БД для SQLite с помощью gui DB Browser for SQLite. Там было несколько простых таблиц, заполненных небольшим количеством данных, буквально записей 10 самое большое. Данные тоже простые - короткие предложения и маленькие цифры. Скопировал эту БД в ассеты, весьма успешно подключился к ней, считал и отобразил на экране.

После этого добавил пару полей, изменил названия и добавил чуть больше данных. И все, с этого момента получаю сообщение вроде
E/SQLiteLog: (11) database corruption at line 66390 of [d2e6722037]
E/SQLiteLog: (11) statement aborts at 8: [select id, code from item_subcategory where id = 10] database disk image is malformed


Сперва подумал может из-за данных, убрал из записей строки со слэшами - не помогло. Подумал, может DB Browser при изменении полей как-то сломал базу и попробовал создать заново - и запросами заранее заготовленными, и руками создавал таблицы и поля. И даже код программы перенес в новый проект (все равно в исходном было много лишнего) - ошибка все та же.

Эмулятор не использую - запускаю через подключенный телефон, но и в эмуляторе была ошибка.

Более подробный список ошибок

E/SQLiteLog: (11) database corruption at line 66390 of [d2e6722037]
E/SQLiteLog: (11) statement aborts at 8: [select id, code from item_subcategory where id = 10] database disk image is malformed

W/asampleprognam: Got a deoptimization request on un-deoptimizable method long android.database.sqlite.SQLiteConnection.nativeExecuteForCursorWindow(long, long, long, int, int, boolean)
E/DefaultDatabaseErrorHandler: Corruption reported by sqlite on database: /data/user/0/com.example.sampleprogname/databases/dbmanual.db
W/asampleprognam: Got a deoptimization request on un-deoptimizable method void sun.nio.fs.UnixNativeDispatcher.stat0(long, sun.nio.fs.UnixFileAttributes)
E/SQLiteDatabase: DB wipe detected: package=com.example.sampleprogname reason=corruption file=/data/user/0/com.example.sampleprogname/databases/dbmanual.db ctime=2023-02-16T12:31:09Z mtime=2023-02-16T12:31:09Z atime=2023-02-16T12:30:17Z checkfile [unable to obtain timestamp]
java.lang.Throwable: STACKTRACE
...
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.sampleprogname, PID: 24638
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.sampleprogname/com.example.sampleprogname.activities.itemcatalog.ItemsCatalogActivity}: android.database.sqlite.SQLiteDatabaseCorruptException: database disk image is malformed (code 11 SQLITE_CORRUPT)


При этом в том же самом DB Browser проверка на ошибки говорит что все в порядке. Я копирую запросы из строк, которые генерируются в приложении, выполняю их отдельно - запросы корректные.
  • Вопрос задан
  • 526 просмотров
Решения вопроса 1
pinkertem
@pinkertem Автор вопроса
В итоге нашел некий код на so, немножко адаптировал и получилось
вот что

public class DbHelper extends SQLiteOpenHelper {
    private static final String DB_NAME = "mydatabase.db";
    private static final String DB_DIRECTORY = "/data/data/com.example.sampleprogname/databases/";
    private static final String DB_FILEPATH = DB_DIRECTORY + DB_NAME;
    private static final int DB_VERSION = 4;

    private SQLiteDatabase db;
    private Context context;
    private static DbHelper dbHelper;

    public static DbHelper getInstance(Context context) {
        if (dbHelper == null) {
            // Хотя context приходит от конкретной активити, но это контекст application
            dbHelper = new DbHelper(context);
        }
        return dbHelper;
    }

    private DbHelper(Context context) {
        super(context, DB_NAME, null, DB_VERSION);
        this.context = context;

        if (dbExists()) {
            opendDb();
        } else {
            createDb();
        }
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
    }

    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {

    }

    public void createDb() {
        if(!dbExists()) {
            this.getReadableDatabase();
            try {
                copyDbFromAssetsToDevice();
            } catch(IOException ex) {
                Log.e("db-copy", "Не удалось скопировать БД на устройство: " + ex.getMessage());
            }
        }
    }

    private void copyDbFromAssetsToDevice() throws IOException {
        try {
            InputStream istream = context.getAssets().open(DB_NAME);
            OutputStream ostream = new FileOutputStream(DB_FILEPATH);

            byte[] buffer = new byte[1024];
            int length;

            while ((length = istream.read(buffer)) > 0){
                ostream.write(buffer, 0, length);
            }

            ostream.flush();
            ostream.close();
            istream.close();
        } catch (Exception e) {
            Log.e("db-copy", e.getMessage());
        }
    }

    private boolean dbExists() {
        boolean dbExist = false;
        try {
            File dbfile = new File(DB_FILEPATH);
            dbExist = dbfile.exists();
        } catch(SQLiteException e) {
            Log.e("Database does not exists", e.getMessage());
        }
        return dbExist;
    }

    public void opendDb() throws SQLException {
        db = SQLiteDatabase.openDatabase(DB_FILEPATH, null, SQLiteDatabase.OPEN_READONLY);
    }

    public synchronized void closeDb() {
        if(db != null) {
            db.close();
        }
        super.close();
    }
}

Некоторые части кажутся мне лишними и абсурдными, но это народные средства против официальной медицины и они просто работают.
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
@402d
начинал с бейсика на УКНЦ в 1988

Скопировал эту БД в ассеты, весьма успешно подключился к ней, считал и отобразил на экране.

В реальности вы использовали prepolation (заполнение начальных данных), а не подключились к базе.
Вы же откуда копировали куски кода. Так вот там перенос данных в созданную на телефоне базу данных.

После этого добавил пару полей, изменил названия и добавил чуть больше данных.

Это действие должно сопровождаться повышением номера информационной схемы и написанием миграции с обной версии в новую.

На стадии разработки можно пропускать этот шаг. Но тогда будут ошибки целостности. Главное коректно между релизами, чтобы работало - пользователи не должны терять свои данные.

А Вам как разработчику тогда потребуется полностью удалять приложение с телефона и ставить на чистую повторно. В конце отладки поставить предыдущий релиз и проверить, что миграция отработала правильно (ну а до первого (еще не опубликованного) имхо лишнее).
З.ы. еще эмулятор может грузиться с записанного снапшота, где сохранилось состояние с установленной прошлой версией.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы