Копирование базы данных SQLite на Oreo - PullRequest
1 голос
/ 21 мая 2019

В одном из моих приложений для Android я использую предварительно заполненную базу данных, которую я копирую при первом использовании.Пока это работает хорошо на всех версиях Android, но с API 28 это не работает.Он копирует базу данных и может впоследствии подключиться к базе данных, но по какой-то причине ни одна из таблиц не доступна.Я получаю сообщение об ошибке, что таблица не существует.

Я проверил базу данных, загрузив ее из эмулятора, и с базой проблем нет.Другое приложение, в котором я создаю базу данных в коде, работает нормально (база данных создается и после нее можно найти таблицы).

Код, который я использую для копирования базы данных:

public void copyDatabase() throws IOException{
    InputStream myInput = mContext.getAssets().open(mDbSource);

    // Path to the just created empty db
    String outFileName = getDbPath() + mDbName;

    //Open the empty db as the output stream
    OutputStream myOutput = new FileOutputStream(outFileName);

    //transfer bytes from the inputfile to the outputfile
    byte[] buffer = new byte[1024];
    int length;
    while ((length = myInput.read(buffer))>0){
        myOutput.write(buffer, 0, length);
    }

    //Close the streams
    myOutput.flush();
    myOutput.close();
    myInput.close();
}

private String getDbPath(){
    return "/data/data/"+mContext.getResources().getString(R.string.package_name)+"/databases/";
}

Что-нибудь изменилосьв API 28 это почему-то не получается?Что может быть причиной этого?

Ответы [ 3 ]

2 голосов
/ 21 мая 2019

Проблема с API 28 в том, что WAL (запись в очередь записи) включена по умолчанию.Если, как это часто бывает, база данных открыта, что часто делается при проверке наличия базы данных, то создаются два файла: -wal и -shm.

Например, в вашем коде это означает, что вы делаете следующее: -

// Path to the just created empty db
String outFileName = getDbPath() + mDbName;

Когда существующая база данных переопределяется копией, файлы -wal и -shmостаются.

Когда в конце концов попытается открыть базу данных как базу данных SQLiteD, возникает несоответствие между базой данных и файлами -wal и -shm.Затем базовое открытие SQLiteDatabase решает, что база данных не может быть открыта, и, таким образом, чтобы обеспечить пригодную для использования базу данных, удаляет и воссоздает базу данных, эффективно опустошая базу данных, и, следовательно, возникает ситуация.1010 *

  1. Переопределите метод onConfigure и заставьте его использовать старый режим журнала (используя [disableWriteAheadLogging] (метод https://developer.android.com/reference/android/arch/persistence/db/SupportSQLiteDatabase#disablewriteaheadlogging)).
  2. Используйте метод открытия SQLiteDatabase, чтобы проверить, существует ли база данных, но проверить, существует ли файл.
  3. В качестве части копии удалите файлы -wal и -shm.

Я бы предположил, что 2 или 3 являются лучшими способами, как: -

  • Нет никаких накладных расходов на ненужное открытие (и создание) базы данных, просто чтобы проверитьего существование и создание каталогов.
  • Приложение, вероятно, будет улучшено благодаря преимуществам WAL по сравнению с ведением журнала.

Все вышеперечисленное Приложение для Android с предварительно заполненной базой данных .

Код решения фактически включает в себя как 2, так и 3 (хотя исправление 3 не требуется) (поскольку исправление 2 не открывает базу данных как базу данных SQLite)).

Сказав, что я считаю, чтоследующий код для проверки, существует ли база данных (исправление 2), лучше ее эквивалента в ответе выше: -

/**
 * Check if the database already exists. NOTE will create the databases folder is it doesn't exist
 * @return true if it exists, false if it doesn't
 */
public static boolean checkDataBase(Context context, String dbname) {

    File db = new File(context.getDatabasePath(dbname).getPath()); //Get the file name of the database
    Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove if publish App
    if (db.exists()) return true; // If it exists then return doing nothing

    // Get the parent (directory in which the database file would be)
    File dbdir = db.getParentFile();
    // If the directory does not exits then make the directory (and higher level directories)
    if (!dbdir.exists()) {
        db.getParentFile().mkdirs();
        dbdir.mkdirs();
    }
    return false;
}
1 голос
/ 22 мая 2019

Да, с android 9 есть два дополнительных файла, которые являются частью базы данных SQLite, и если вы скопируете базу данных, перекрывающую существующую базу данных, и не удалите эти два других файла первыми, то база данных будет считаться поврежденной.,Смотрите этот ответ: Отправьте приложение для Android с предварительно заполненной базой данных

Но @MikeT должен получить кредит, так как связанный ответ был его с самого начала !!

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

Я не могу быть уверен, пока не увижу logcat.Тем не менее, я думаю, что при доступе к вашей базе данных у вас может отсутствовать какое-то разрешение.

Я хотел бы спросить, есть ли у вас в вашем файле AndroidManifest.xml следующий провайдер для предоставления вашему приложению прав доступа к базе данных.Вот пример файла манифеста с поставщиком.

<provider
    android:name=".DatabaseContentProvider"
    android:authorities="your.application.package.name"
    android:exported="false" />

И вам нужен класс DatabaseContentProvider, подобный следующему.

import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;

public class DatabaseContentProvider extends ContentProvider {

    @Nullable
    @Override
    public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
        return null;
    }

    @Nullable
    @Override
    public Cursor query(@NonNull Uri uri,
                        @Nullable String[] projection,
                        @Nullable String selection,
                        @Nullable String[] selectionArgs,
                        @Nullable String sortOrder) {
        return null;
    }

    @Override
    public boolean onCreate() {
        return true;
    }

    @Override
    public int update(@NonNull Uri uri, @Nullable ContentValues values,
                      @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Override
    public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
        return 0;
    }

    @Nullable
    @Override
    public String getType(@NonNull Uri uri) {
        return null;
    }
}

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...