Android P - «SQLite: нет такой ошибки таблицы» после копирования базы данных из ресурсов - PullRequest
0 голосов
/ 23 мая 2018

У меня есть база данных, сохраненная в папке активов моего приложения, и я копирую базу данных, используя приведенный ниже код при первом запуске приложения.

inputStream = mContext.getAssets().open(Utils.getDatabaseName());

        if(inputStream != null) {

            int mFileLength = inputStream.available();

            String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();

            // Save the downloaded file
            output = new FileOutputStream(filePath);

            byte data[] = new byte[1024];
            long total = 0;
            int count;
            while ((count = inputStream.read(data)) != -1) {
                total += count;
                if(mFileLength != -1) {
                    // Publish the progress
                    publishProgress((int) (total * 100 / mFileLength));
                }
                output.write(data, 0, count);
            }
            return true;
        }

Приведенный выше код запускается без проблем, но при попытке запросаБаза данных, которую вы получаете SQLite: нет таких исключений таблицы.

Эта проблема возникает только в Android P, все более ранние версии Android работают правильно.

Это известная проблема с Android P или есть что-тоизменилось?

Ответы [ 13 ]

0 голосов
/ 01 августа 2019

Я не могу комментировать принятый ответ, поэтому я должен открыть новый ответ.

mContext.getDatabasePath () НЕ открывает соединение с базой данных, ему даже не требуется существующее имя файла для успеха (см.sources / android-28 / android / app / ContextImpl.java):

@Override
public File getDatabasePath(String name) {
    File dir;
    File f;

    if (name.charAt(0) == File.separatorChar) {
        // snip
    } else {
        dir = getDatabasesDir();
        f = makeFilename(dir, name);
    }

    return f;
}

private File makeFilename(File base, String name) {
    if (name.indexOf(File.separatorChar) < 0) {
        return new File(base, name);
    }
    throw new IllegalArgumentException(
            "File " + name + " contains a path separator");
}
0 голосов
/ 15 августа 2019

В версии P основным изменением является WAL (запись в журнал записи).Требуются следующие два шага:

  1. Отключите то же самое с помощью следующей строки в config.xml в папке значений в разделе ресурсов.

false

Внести следующие изменения в класс DBAdapter в методе createDatabase.В противном случае происходит сбой телефонов с более ранними версиями Android.

private void createDataBase () создает IOException {if (android.os.Build.VERSION.SDK_INT

try {           
    copyDataBase();            
} catch (IOException e) {           
    throw new RuntimeException(e);
}

}

0 голосов
/ 08 мая 2019

Самый простой ответ - использовать следующую строку для пути к файлу базы данных в Android PIE и выше:

DB_NAME="xyz.db";
DB_Path = "/data/data/" + BuildConfig.APPLICATION_ID + "/databases/"+DB_NAME;
0 голосов
/ 24 июля 2019

Вот идеальное решение для этой проблемы:

Просто переопределите этот метод в вашем SQLiteOpenHelper классе:

@Override
public void onOpen(SQLiteDatabase db) {
    super.onOpen(db);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
        db.disableWriteAheadLogging();
    }
}
0 голосов
/ 05 января 2019

Решение без отключения WAL

Android 9 представляет специальный режим SQLiteDatabase под названием Совместимость WAL (вход в систему с опережением записи), которыйпозволяет базе данных использовать «journal_mode = WAL», сохраняя при этом поведение сохранения не более одного соединения на базу данных.

Подробнее здесь:
https://source.android.com/devices/tech/perf/compatibility-wal

Режим SQLite WALподробно объясняется здесь:
https://www.sqlite.org/wal.html

Начиная с официальных документов, режим WAL добавляет второй файл базы данных с именами databasename и "-wal",Поэтому, если ваша база данных называется «data.db», она называется «data-wal.db» в том же каталоге.

Теперь решение заключается в том, чтобы сохранять и восстанавливать ОБА файлов (данных.db и data-wal.db) на Android 9.

После этого он работает как в предыдущих версиях.

0 голосов
/ 16 января 2019

К сожалению, принятый ответ просто «срабатывает» в очень конкретных случаях, но он не дает последовательно работающего совета, чтобы избежать такой ошибки в Android 9.

Вот оно:

  1. Для доступа к базе данных в вашем приложении должен быть один экземпляр класса SQLiteOpenHelper.
  2. Если вам нужно перезаписать / скопировать базу данных, закройте базу данных (и закройте все соединения с этой базой данных), используяМетод SQLiteOpenHelper.close () этого экземпляра И больше не используйте этот экземпляр SQLiteOpenHelper.

После вызова close () закрываются не только все подключения к базе данных, но и дополнительные файлы журнала базы данных.залил в основной файл .sqlite и удалил.Таким образом, у вас есть только один файл database.sqlite, готовый к перезаписи или копированию.

После копирования / перезаписи и т. Д. Создайте новый синглтон SQLiteOpenHelper, который методом getWritableDatabase () вернет новый экземпляр базы данных SQLite!И используйте его до следующего раза, когда вам понадобится скопировать / переписать вашу базу данных ...

Этот ответ помог мне выяснить это: https://stackoverflow.com/a/35648781/297710

У меня была эта проблемав Android 9 в моем приложении AndStatus https://github.com/andstatus/andstatus, которое имеет довольно большой набор автоматических тестов, которые последовательно воспроизводили «SQLiteException: no only table» в эмуляторе Android 9 до этого коммита: https://github.com/andstatus/andstatus/commit/1e3ca0eee8c9fbb8f6326b72dc4c393143a70538 Так что если выочень любопытно, вы можете запустить все тесты до и после этого коммита, чтобы увидеть разницу.

0 голосов
/ 25 сентября 2018

Аналогичная проблема, затронуто только устройство Android P.Все предыдущие версии без проблем.

Отключено автоматическое восстановление на устройствах Android 9.

Мы сделали это для устранения неполадок.Не рекомендовал бы для производственных случаев.

Автоматическое восстановление помещало копию файла базы данных в каталог данных до вызова функции копирования базы данных в помощнике базы данных.Поэтому a file.exists () вернул true.

В базе данных, для которой было выполнено резервное копирование с устройства разработки, отсутствует таблица.Поэтому «таблица не найдена» была действительно правильной.

0 голосов
/ 12 ноября 2018

Мои проблемы с Android P были решены путем добавления this.close () после this.getReadableDatabase () в методе createDataBase (), как показано ниже.

private void createDataBase() throws IOException {
    this.getReadableDatabase();
    this.close(); 
    try {           
        copyDataBase();            
    } catch (IOException e) {           
        throw new RuntimeException(e);
    }
}
0 голосов
/ 21 августа 2018

У меня была похожая проблема, и я решил эту проблему, добавив это в мой SQLiteOpenHelper

    @Override
    public void onOpen(SQLiteDatabase db) {
        super.onOpen(db);
        db.disableWriteAheadLogging();
    }

Очевидно, что Android P отличает журнал PRAGMA Log.До сих пор не знаю, будут ли побочные эффекты, но, похоже, работает!

0 голосов
/ 17 августа 2018

Я столкнулся с подобной проблемой.Я копировал базу данных, но не из ресурса.Я обнаружил, что проблема не имеет никакого отношения к коду моего файла базы данных.И при этом это не имело отношения к файлам, оставленным открытым, не закрытым, очищением или синхронизацией.Мой код обычно перезаписывает существующую неоткрытую базу данных.То, что кажется новым / отличным от Android Pie и отличается от предыдущих выпусков Android, заключается в том, что когда Android Pie создает базу данных SQLite, он устанавливает значение journal_mode в WAL (запись в журнал с опережением записи) по умолчанию.Я никогда не использовал режим WAL, и в документации по SQLite сказано, что journal_mode должен быть по умолчанию DELETE.Проблема заключается в том, что если я перезаписываю существующий файл базы данных, назовем его my.db, журнал предварительной записи my.db-wal все еще существует и эффективно «переопределяет» то, что находится в недавно скопированном файле my.db.Когда я открывал свою базу данных, таблица sqlite_master обычно содержала только строку для android_metadata.Все таблицы, которые я ожидал, отсутствовали.Мое решение состоит в том, чтобы просто установить journal_mode обратно на DELETE после открытия базы данных, особенно при создании новой базы данных с Android Pie.

PRAGMA journal_mode = DELETE;

Возможно, WAL лучше, и, возможно, некоторыеспособ закрыть базу данных, чтобы не мешать записи с опережением записи, но мне действительно не нужен WAL и он не нужен для всех предыдущих версий Android.

...