База данных, скопированная из ресурсов, пуста - PullRequest
0 голосов
/ 01 мая 2018

У меня есть предварительно заполненная база данных SQLite, которую я копирую из apk при установке. База данных успешно копируется, но затем кажется пустой (я получаю исключение таблицы SQLite при запросе).

Я использую этот код для обработки файловых операций базы данных:

public class ExternalDbOpenHelper extends SQLiteOpenHelper {

    //Path to the device folder with databases
    public static String DB_PATH;

    //Database file name
    public static String DB_NAME;
    public SQLiteDatabase database;
    public final Context context;

    public SQLiteDatabase getDb() {
        return database;
    }

    public ExternalDbOpenHelper(Context context, String databaseName) {
        super(context, databaseName, null, 1);
        this.context = context;
//Write a full path to the databases of your application
        String packageName = context.getPackageName();
        DB_PATH = String.format("//data//data//%s//databases//", packageName);
        DB_NAME = databaseName;
        openDataBase();
    }

    //This piece of code will create a database if it’s not yet created
    public void createDataBase() {
        boolean dbExist = checkDataBase();
        if (!dbExist) {
            this.getReadableDatabase();
            try {
                copyDataBase();
            } catch (IOException e) {
                Log.e(this.getClass().toString(), "Copying error");
                throw new Error("Error copying database!");
            }
        } else {
            Log.i(this.getClass().toString(), "Database already exists");
        }
    }

    //Performing a database existence check
    private boolean checkDataBase() {
        SQLiteDatabase checkDb = null;
        try {
            String path = DB_PATH + DB_NAME;
            checkDb = SQLiteDatabase.openDatabase(path, null,
                    SQLiteDatabase.OPEN_READONLY);
        } catch (SQLException e) {
            Log.e(this.getClass().toString(), "Error while checking db");
        }
        //Android doesn’t like resource leaks, everything should
        // be closed
        if (checkDb != null) {
            checkDb.close();
        }
        return checkDb != null;
    }

    //Method for copying the database
    private void copyDataBase() throws IOException {
        //Open a stream for reading from our ready-made database
        //The stream source is located in the assets
        InputStream externalDbStream = context.getAssets().open(DB_NAME);

        //Path to the created empty database on your Android device
        String outFileName = DB_PATH + DB_NAME;

        //Now create a stream for writing the database byte by byte
        OutputStream localDbStream = new FileOutputStream(outFileName);

        //Copying the database
        byte[] buffer = new byte[1024];
        int bytesRead;
        while ((bytesRead = externalDbStream.read(buffer)) > 0) {
            localDbStream.write(buffer, 0, bytesRead);
        }
        //Don’t forget to close the streams
        localDbStream.close();
        externalDbStream.close();
    }

    public SQLiteDatabase openDataBase() throws SQLException {
        String path = DB_PATH + DB_NAME;
        if (database == null) {
            createDataBase();
            database = SQLiteDatabase.openDatabase(path, null,
                    SQLiteDatabase.OPEN_READWRITE);
        }
        return database;
    }

    @Override
    public synchronized void close() {
        if (database != null) {
            database.close();
        }
        super.close();
    }
    @Override
    public void onCreate(SQLiteDatabase db) {}
    @Override
    public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {}
}

Я использовал этот точный код в 3 почти одинаковых приложениях без проблем. Любые идеи о том, почему я могу столкнуться с проблемами с тем же кодом в новом приложении? Приложение предназначено / строится на уровне API 25.

Ответы [ 2 ]

0 голосов
/ 02 мая 2018

Функция ниже поможет вам скопировать вашу базу данных на SD-карту или любое другое место. сейчас он копируется на SD-карту.

public static void copyDbToSdCard(Context context) {
        try {
            File sd = Environment.getExternalStorageDirectory();
            //File data = Environment.getDataDirectory();

            if (sd.canWrite()) {
                String currentDBPath = "/data/data/" + context.getPackageName() + "/databases/JSandeshEmpTracker";
                String backupDBPath = "db_backup.db";
                File currentDB = new File(currentDBPath);
                File backupDB = new File(sd, backupDBPath);

                if (currentDB.exists()) {
                    FileChannel src = new FileInputStream(currentDB).getChannel();
                    FileChannel dst = new FileOutputStream(backupDB).getChannel();
                    dst.transferFrom(src, 0, src.size());
                    src.close();
                    dst.close();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
0 голосов
/ 01 мая 2018

Без подробностей журнала проблему можно угадать только.

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

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

05-01 20:22:41.988 1309-1309/? E/AndroidRuntime: FATAL EXCEPTION: main java.lang.Error: Error copying database! at .......

База данных, однако, создана, но пуста (как таковая). Таким образом, последующий запуск не будет пытаться скопировать БД, поскольку БД теперь существует.

Если это не решит проблему, то вы либо копируете файл из папки ресурсов, в который не входит упомянутая таблица, либо используете неверное имя таблицы.

Чтобы определить, что на самом деле было скопировано, можно выполнить опрос таблицы sqlite_master. Тем не менее, вот класс , который можно скопировать, который имеет метод logDatabaseInfo, который делает это, и немного больше. Просто создайте CommonSQLiteUtilities.java и скопируйте код из ответа с заголовком Добавление 1 - logDatabaseInfo , а затем сразу после создания экземпляра ExternalDbOpenHelper вызовите метод logDatabaseInfo, например. : -

    ExternalDbOpenHelper extdbhlpr = new ExternalDbOpenHelper(this,"your_database_name");
    CommonSQLiteUtilities.logDatabaseInfo(extdbhlpr.getWritableDatabase());

Если у вас пустая база данных, logDatabaseInfo будет записывать что-то вроде: -

05-01 20:43:59.424 1706-1706/? I/class mjt.adapters.ExternalDbOpenHelper: Database already exists
05-01 20:43:59.424 1706-1706/? D/SQLITE_CSU: DatabaseList Row 1 Name=main File=/data/data/mjt.adapters/databases/my_db
    Database Version = 1
    Table Name = android_metadata Created Using = CREATE TABLE android_metadata (locale TEXT)
05-01 20:43:59.428 1706-1706/? D/SQLITE_CSU: Table = android_metadata ColumnName = locale ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
  • Обратите внимание, что первая строка выдается при создании экземпляра ExternalDbOpenhelper

Ниже приведен пример первого запуска (при копировании БД из папки активов) и заполнении базы данных: -

05-01 20:50:01.396 1784-1784/mjt.adapters E/SQLiteLog: (14) cannot open file at line 30174 of [00bb9c9ce4]
    (14) os_unix.c:30174: (2) open(//data//data//mjt.adapters//databases//my_db) - 
05-01 20:50:01.404 1784-1786/mjt.adapters D/dalvikvm: GC_CONCURRENT freed 211K, 9% free 6187K/6791K, paused 11ms+1ms, total 27ms
05-01 20:50:01.404 1784-1784/mjt.adapters E/SQLiteDatabase: Failed to open database '//data//data//mjt.adapters//databases//my_db'.
    android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database
        at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209)
        at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193)
        at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185)
        at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177)
        at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804)
        at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694)
        at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669)
        at mjt.adapters.ExternalDbOpenHelper.checkDataBase(ExternalDbOpenHelper.java:59)
        at mjt.adapters.ExternalDbOpenHelper.createDataBase(ExternalDbOpenHelper.java:40)
        at mjt.adapters.ExternalDbOpenHelper.openDataBase(ExternalDbOpenHelper.java:98)
        at mjt.adapters.ExternalDbOpenHelper.<init>(ExternalDbOpenHelper.java:35)
        at mjt.adapters.MainActivity.onCreate(MainActivity.java:39)
        at android.app.Activity.performCreate(Activity.java:5008)
        at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1079)
        at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2023)
        at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2084)
        at android.app.ActivityThread.access$600(ActivityThread.java:130)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1195)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:4745)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:511)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
        at dalvik.system.NativeStart.main(Native Method)
05-01 20:50:01.404 1784-1784/mjt.adapters E/class mjt.adapters.ExternalDbOpenHelper: Error while checking db
05-01 20:50:01.444 1784-1784/mjt.adapters D/SQLITE_CSU: DatabaseList Row 1 Name=main File=/data/data/mjt.adapters/databases/my_db
    Database Version = 0
    Table Name = user_table Created Using = CREATE TABLE user_table (ID TEXT, OCC INTEGER)
    Table = user_table ColumnName = ID ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
    Table = user_table ColumnName = OCC ColumnType = INTEGER Default Value = null PRIMARY KEY SEQUENCE = 0
    Table Name = sqlite_sequence Created Using = CREATE TABLE sqlite_sequence(name,seq)
    Table = sqlite_sequence ColumnName = name ColumnType =  Default Value = null PRIMARY KEY SEQUENCE = 0
    Table = sqlite_sequence ColumnName = seq ColumnType =  Default Value = null PRIMARY KEY SEQUENCE = 0
    Table Name = tasks Created Using = CREATE TABLE tasks (_id INTEGER PRIMARY KEY AUTOINCREMENT, title TEXT date DATE, new_colum INTEGER DEFAULT 0)
    Table = tasks ColumnName = _id ColumnType = INTEGER Default Value = null PRIMARY KEY SEQUENCE = 1
    Table = tasks ColumnName = title ColumnType = TEXT date DATE Default Value = null PRIMARY KEY SEQUENCE = 0
    Table = tasks ColumnName = new_colum ColumnType = INTEGER Default Value = 0 PRIMARY KEY SEQUENCE = 0
    Table Name = Words Created Using = CREATE TABLE Words (Word TEXT, LIKEWORD TEXT)
05-01 20:50:01.448 1784-1784/mjt.adapters D/SQLITE_CSU: Table = Words ColumnName = Word ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
    Table = Words ColumnName = LIKEWORD ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
    Table Name = android_metadata Created Using = CREATE TABLE android_metadata (locale TEXT)
    Table = android_metadata ColumnName = locale ColumnType = TEXT Default Value = null PRIMARY KEY SEQUENCE = 0
  • Примечание трассировка стека происходит из-за способа проверки существования базы данных (лично я проверяю, существует ли файл, чтобы вы не получили трассировку стека). Таким образом, вы ожидаете эту трассировку стека, когда БД фактически скопирована из папки активов.
  • Как видите, таблицы перечислены вместе со столбцами.
  • Примечание android_metadata и любая таблица, начинающаяся с sqlite_, являются внутренними таблицами (android_metadata создается SQLiteOpenHelper, таблицы sqlite_ являются таблицами SQLite).
...