Приложение для Android с предварительно заполненной базой данных - PullRequest
2 голосов
/ 06 мая 2019

Вот мой код,

public DBHelper(Context context) {
    super(context, DB_NAME, null, 2);
    this.context = context;
    DB_PATH = context.getDatabasePath(DB_NAME).getAbsolutePath();
}

@Override
public void onCreate(SQLiteDatabase db) {
    createDataBase();
}

private void createDataBase() {
    boolean dbExist = checkDataBase();
    if (!dbExist) {
        copyDataBase();
    }
}

private boolean checkDataBase() {
    System.out.println("DB_PATH : " + DB_PATH);
    File dbFile = new File(DB_PATH);
    return dbFile.exists();
}

private void copyDataBase() {
    Log.i("Database",
            "New database is being copied to device!");
    byte[] buffer = new byte[1024];
    OutputStream myOutput;
    int length;
    InputStream myInput;
    try {
        myInput = context.getAssets().open(DB_NAME);
        myOutput = new FileOutputStream(DB_PATH);
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        myOutput.close();
        myOutput.flush();
        myInput.close();
        Log.i("Database",
                "New database has been copied to device!");
    } catch (IOException e) {
        e.printStackTrace();
    }
}

Все работает нормально, и я даже получаю журнал New database has been copied to device!, но когда я пытаюсь прочитать данные из базы данных, я получаю исключение no such table.

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

1 Ответ

4 голосов
/ 06 мая 2019

Предполагая, что база данных, которую вы скопировали в папку активов, содержит таблицы, я полагаю, что ваша проблема в том, что вы создаете экземпляр экземпляра DBHelper, а затем вы неявно открыли базу данных неявным или явным образом вызывая getWritableDatabase или getReadableDatabase, а затем используют метод onCreate для инициации копирования.

Если это так, то get ????ableDatabase создаст пустую базу данных, копия перезаписывает это, но в более поздних версиях, Android 9+, файлы -shm и -wal остаются такими же, как и когда база данных затем открыт, затем из-за того, что файлы -shm и -wal не соответствуют исходной пустой базе данных, обнаруживается повреждение, поэтому создается новая пустая база данных, поскольку код SDK пытается предоставить пригодную для использования базу данных.

Есть 3 исправления.

  • используйте метод disableWriteAheadLogging , переопределяя метод onConfigure класса SQLiteOpenHelper. Затем используется старый режим журнала.

  • Убедитесь, что getWritableDatabase / getReadableDatabase не вызывается. Это можно сделать, убедившись, что копия сделана при создании экземпляра DBHelper.

  • Обеспечение удаления файлов -wal и -shm, если они существуют на момент копирования.

Использование первого, возможно, только задерживает неизбежное и на самом деле не рекомендуется, так как не использует преимущества режима WAL.

Следующая версия вашего DBHelper включает второе исправление, а также в качестве меры предосторожности третье исправление: -

public class DBHelper extends SQLiteOpenHelper {

    public static final  String DB_NAME = "myDBName";
    public static String DB_PATH;

    Context context;

    public DBHelper(Context context) {
        super(context, DB_NAME, null, 2);
        this.context = context;
        //<<<<<<<<<< ADDED (moved from createDatabase) 1st Fix >>>>>>>>>>
        DB_PATH = context.getDatabasePath(DB_NAME).getAbsolutePath();
        if (!checkDataBase()) {
            copyDataBase();
        }
        //<<<<<<<<<< END OF ADDED CODE >>>>>>>>>>
        this.getWritableDatabase(); //<<<<<<<<<< Added to force an open after the copy - not essential
    }

    @Override
    public void onCreate(SQLiteDatabase db) {
        //createDataBase(); <<<<<<<<<< relying on this was the cause of the issue >>>>>>>>>>
    }

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

    }

    //<<<<<<<<<< NOT NEEDED AND SHOULD NOT BE CALLED >>>>>>>>>
    private void createDataBase() {
        boolean dbExist = checkDataBase();
        if (!dbExist) {
            copyDataBase();
        }
    }

    private boolean checkDataBase() {
        System.out.println("DB_PATH : " + DB_PATH);
        File dbFile = new File(DB_PATH);
        if (dbFile.exists()) return true;
        //<<<<<<<<<< ADDED to create the databases directory if it doesn't exist >>>>>>>>>>
        //it may be that getWritableDatabase was used to circumvent the issue that the copy would fail in the databases directory does not exist, hence this fix is included
        if (!new File(dbFile.getParent()).exists()) {
            new File(dbFile.getParent()).mkdirs();
        }
        return false;
    }

    private void copyDataBase() {
        Log.i("Database",
                "New database is being copied to device!");
        byte[] buffer = new byte[1024];
        //<<<<<<<<<< ADDED to delete wal and shm files if they exist (3rd fix) >>>>>>>>>>
        File dbDirectory = new File(new File(DB_PATH).getParent());
        File dbwal = new File(dbDirectory.getPath() + File.separator + "-wal");
        if (dbwal.exists()) {
            dbwal.delete();
        }
        File dbshm = new File(dbDirectory.getPath() + File.separator + "=shm");
        if (dbshm.exists()) {
            dbshm.delete();
        }
        //<<<<<<<<<< END OF ADDED CODE >>>>>>>>>>

        OutputStream myOutput;
        int length;
        InputStream myInput;
        try {
            myInput = context.getAssets().open(DB_NAME);
            myOutput = new FileOutputStream(DB_PATH);
            while ((length = myInput.read(buffer)) > 0) {
                myOutput.write(buffer, 0, length);
            }
            myOutput.close();
            myOutput.flush();
            myInput.close();
            Log.i("Database",
                    "New database has been copied to device!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Это было протестировано как на Android 5, так и на Android 10 с использованием следующего кода из действия (вместе с дополнительным кодом для выгрузки схемы (обратите внимание, очевидно, что НЕ ваша база данных, а та, которая была доступна)): -

DBHelper mDBHlpr;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mDBHlpr = new DBHelper(this);

Результат по логу: -

2019-05-07 06:20:53.148 I/System.out: DB_PATH : /data/user/0/soa.usingyourownsqlitedatabaseblog/databases/myDBName
2019-05-07 06:20:53.148 I/Database: New database is being copied to device!
2019-05-07 06:20:53.149 I/Database: New database has been copied to device!
2019-05-07 06:20:53.168 I/System.out: >>>>> Dumping cursor android.database.sqlite.SQLiteCursor@e3fe34f
2019-05-07 06:20:53.169 I/System.out: 0 {
2019-05-07 06:20:53.169 I/System.out:    type=table
2019-05-07 06:20:53.169 I/System.out:    name=Categories
2019-05-07 06:20:53.169 I/System.out:    tbl_name=Categories
2019-05-07 06:20:53.169 I/System.out:    rootpage=2
2019-05-07 06:20:53.169 I/System.out:    sql=CREATE TABLE "Categories" (
2019-05-07 06:20:53.169 I/System.out:   "not_id" integer NOT NULL,
2019-05-07 06:20:53.169 I/System.out:   "CategoryLabel" TEXT,
2019-05-07 06:20:53.169 I/System.out:   "Colour" integer,
2019-05-07 06:20:53.169 I/System.out:   PRIMARY KEY ("not_id")
2019-05-07 06:20:53.169 I/System.out: )
2019-05-07 06:20:53.170 I/System.out: }
2019-05-07 06:20:53.170 I/System.out: 1 {
2019-05-07 06:20:53.170 I/System.out:    type=table
2019-05-07 06:20:53.170 I/System.out:    name=Content
2019-05-07 06:20:53.170 I/System.out:    tbl_name=Content
2019-05-07 06:20:53.170 I/System.out:    rootpage=3
2019-05-07 06:20:53.170 I/System.out:    sql=CREATE TABLE "Content" (
2019-05-07 06:20:53.170 I/System.out:   "again_not_id" INTEGER NOT NULL,
2019-05-07 06:20:53.170 I/System.out:   "Text" TEXT,
2019-05-07 06:20:53.170 I/System.out:   "Source" VARCHAR,
2019-05-07 06:20:53.170 I/System.out:   "Category" integer,
2019-05-07 06:20:53.170 I/System.out:   "VerseOrder" integer,
2019-05-07 06:20:53.170 I/System.out:   PRIMARY KEY ("again_not_id")
2019-05-07 06:20:53.170 I/System.out: )
2019-05-07 06:20:53.170 I/System.out: }
2019-05-07 06:20:53.171 I/System.out: 2 {
2019-05-07 06:20:53.171 I/System.out:    type=table
2019-05-07 06:20:53.171 I/System.out:    name=android_metadata
2019-05-07 06:20:53.171 I/System.out:    tbl_name=android_metadata
2019-05-07 06:20:53.171 I/System.out:    rootpage=4
2019-05-07 06:20:53.171 I/System.out:    sql=CREATE TABLE android_metadata (locale TEXT)
2019-05-07 06:20:53.171 I/System.out: }
2019-05-07 06:20:53.171 I/System.out: <<<<<
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...