не удается открыть файл sqlite в папке с ресурсами для копирования на устройство

Это моя первая заявка на Android. Я увидел несколько вопросов по этому поводу, но ни один из них не нашел для меня решения.

У меня есть база данных в папке с активами с именем fmdb.sqlite. Я использовал пример кода по этой ссылке: http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/

Когда я перехожу к строке, которая должна получить поток в файл sqlite в папке с ресурсами (метод copyDataBase), я получаю исключение «java.io.FileNotFoundException: fmdb.sqlite».

Кто-нибудь может сказать мне, что я делаю не так? Вот мой класс.

public class DatabaseOpener extends SQLiteOpenHelper {

// If you change the database schema, you must increment the database version.
public static final int DATABASE_VERSION = 1;

//The Android's default system path of your application database.
public static String DB_PATH;

public static String DB_NAME = "fmdb.sqlite";

private SQLiteDatabase myDataBase;

private final Context myContext;



public DatabaseOpener(Context context) throws IOException {
    super(context, DB_NAME, null, DATABASE_VERSION);
    myContext = context; 
}

/**
 * Check if the database already exists to avoid re-copying the file each time you open the application.
 * @return true if it exists, false if it doesn't
 */
   private boolean checkDataBase(){

       SQLiteDatabase checkDB = null;

       try{
       String myPath = DB_PATH;
       checkDB = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

       }catch(SQLiteException e){

       //database does't exist yet.

       }

       if(checkDB != null){

       checkDB.close();

   }

   return checkDB != null ? true : false;
   }

   /**
    * Creates a empty database on the system and rewrites it with your own database.
    * */
  public void createDataBase() throws IOException{

      boolean dbExist = checkDataBase();

      if(dbExist){
          //do nothing - database already exist
          }else{

          //By calling this method and empty database will be created into the default system path
          //of your application so we are gonna be able to overwrite that database with our database.
          this.getReadableDatabase();

          try {

          copyDataBase();

          } catch (IOException e) {

          throw new Error("Error copying database");

          }
      }

  }

  /**
   * Copies your database from your local assets-folder to the just created empty database in the
   * system folder, from where it can be accessed and handled.
   * This is done by transfering bytestream.
   * */
 private void copyDataBase() throws IOException{

     //Open your local db as the input stream
     InputStream myInput = myContext.getAssets().open(DB_NAME);

     // Path to the just created empty db
     String outFileName = DB_PATH;

     //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();

 }

 public SQLiteDatabase openDataBase() throws SQLException{

    //Open the database
    String myPath = DB_PATH;// + DB_NAME;
    myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.OPEN_READONLY);

    return myDataBase;
}

 @Override
public synchronized void close() {

    if(myDataBase != null)
    myDataBase.close();

    super.close();

}



@Override
public void onCreate(SQLiteDatabase db) {
    // TODO Auto-generated method stub

}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    // TODO Auto-generated method stub

}

}

Редактировать 2 Вот файл LogCat. Я удалил и перезапустил программу. теперь речь идет об открытии файла db для записи, я очень признателен за вашу помощь!

09-29 17:59:19.540: W/SQLiteAssetHelper(27063): copying database from assets... 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): Couldn't open fmdb.sqlite for writing (will try read-only): 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): com.readystatesoftware.sqliteasset.SQLiteAssetHelper$SQLiteAssetException: Missing databases/fmdb.sqlite file (or .zip, .gz archive) in assets, or target folder not writable 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.content.res.AssetManager.openAsset(Native Method) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.content.res.AssetManager.open(AssetManager.java:315) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.content.res.AssetManager.open(AssetManager.java:289) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.copyDatabaseFromAssets(SQLiteAssetHelper.java:436) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.createOrOpenDatabase(SQLiteAssetHelper.java:400) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getWritableDatabase(SQLiteAssetHelper.java:176) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getReadableDatabase(SQLiteAssetHelper.java:254) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.friendlymeter.dal.MyDatabase.getSample(MyDatabase.java:27) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.friendlymeter.app.MainActivity$2.onClick(MainActivity.java:105) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.view.View.performClick(View.java:4204) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.view.View$PerformClick.run(View.java:17355) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.os.Handler.handleCallback(Handler.java:725) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.os.Handler.dispatchMessage(Handler.java:92) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.os.Looper.loop(Looper.java:137) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at android.app.ActivityThread.main(ActivityThread.java:5041) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at java.lang.reflect.Method.invokeNative(Native Method) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at java.lang.reflect.Method.invoke(Method.java:511) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) 09-29 17:59:19.689: E/SQLiteAssetHelper(27063): at dalvik.system.NativeStart.main(Native Method) 09-29 17:59:19.709: E/SQLiteLog(27063): (14) cannot open file at line 30176 of [00bb9c9ce4] 09-29 17:59:19.709: E/SQLiteLog(27063): (14) os_unix.c:30176: (2) open(/data/data/com.friendlymeter.app/databases/fmdb.sqlite) - 09-29 17:59:19.909: D/dalvikvm(27063): GC_CONCURRENT freed 130K, 9% free 2652K/2904K, paused 9ms+15ms, total 171ms 09-29 17:59:20.019: E/SQLiteDatabase(27063): Failed to open database '/data/data/com.friendlymeter.app/databases/fmdb.sqlite'. 09-29 17:59:20.019: E/SQLiteDatabase(27063): android.database.sqlite.SQLiteCantOpenDatabaseException: unknown error (code 14): Could not open database 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnection.nativeOpen(Native Method) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:209) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:193) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:463) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:185) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:177) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:804) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:789) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:694) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:669) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.readystatesoftware.sqliteasset.SQLiteAssetHelper.getReadableDatabase(SQLiteAssetHelper.java:264) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.friendlymeter.dal.MyDatabase.getSample(MyDatabase.java:27) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.friendlymeter.app.MainActivity$2.onClick(MainActivity.java:105) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.view.View.performClick(View.java:4204) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.view.View$PerformClick.run(View.java:17355) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.os.Handler.handleCallback(Handler.java:725) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.os.Handler.dispatchMessage(Handler.java:92) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.os.Looper.loop(Looper.java:137) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at android.app.ActivityThread.main(ActivityThread.java:5041) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at java.lang.reflect.Method.invokeNative(Native Method) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at java.lang.reflect.Method.invoke(Method.java:511) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) 09-29 17:59:20.019: E/SQLiteDatabase(27063): at dalvik.system.NativeStart.main(Native Method)

person amazingwolf    schedule 23.09.2014    source источник
comment
используйте SQLiteAssetHelper для упаковки базы данных в качестве ресурса.   -  person CommonsWare    schedule 24.09.2014
comment
DB_PATH всегда пуст. Как должен работать этот код?   -  person greenapps    schedule 24.09.2014
comment
@greenapps есть статический раздел внутри класса, который инициализирует DB_PATH (не включен в код, но он есть ...)   -  person amazingwolf    schedule 25.09.2014


Ответы (2)


Я на 100% согласен с комментарием CommonsWare. Самый простой способ сделать это - использовать SQLiteAssetHelper. Я отправлю это как ответ, но это 100% копия, вставленная по ссылке выше. Это все, что вам нужно знать, чтобы использовать эту библиотеку.

SQLiteAssetHelper задуман как альтернатива SQLiteOpenHelper фреймворка. Пожалуйста, ознакомьтесь с поведением и жизненным циклом этого класса.

Расширьте SQLiteAssetHelper, как обычно, SQLiteOpenHelper, предоставив конструктору имя базы данных и номер версии:

public class MyDatabase extends SQLiteAssetHelper {

    private static final String DATABASE_NAME = "northwind.db";
    private static final int DATABASE_VERSION = 1;

    public MyDatabase(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }
}

SQLiteAssetHelper использует соглашения об именах файлов и папок. Папка с вашими ресурсами будет либо в корне вашего проекта, либо в src / main, если вы используете структуру проекта gradle по умолчанию. Как минимум, вы должны предоставить следующее:

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

assets / databases / northwind.db Более ранние версии этой библиотеки требовали, чтобы актив базы данных был сжат в ZIP-архив. Это больше не является требованием, но все еще поддерживается. Приложения, все еще ориентированные на Gingerbread (API 10) или ниже, должны продолжать предоставлять сжатый архив, чтобы гарантировать, что большие файлы базы данных не будут повреждены во время процесса упаковки. Также поддерживается более удобный для Linux формат GZIP. Соглашения об именах, использующие приведенный выше пример, следующие:

ZIP: assets / databases / northwind.db.zip (один файл базы данных SQLite должен быть единственным файлом в архиве) GZIP: assets / databases / northwind.db.gz База данных будет извлечена из ресурсов и скопирована на место внутри архива. каталог личных данных вашего приложения. Если вы предпочитаете хранить файл базы данных в другом месте (например, во внешнем хранилище), вы можете использовать альтернативный конструктор, чтобы указать путь к хранилищу. Вы должны убедиться, что этот путь доступен и доступен для записи всякий раз, когда вашему приложению требуется доступ к базе данных.

super(context, DATABASE_NAME, context.getExternalFilesDir(null).getAbsolutePath(), null, DATABASE_VERSION);

База данных становится доступной для использования при первом вызове getReadableDatabase () или getWritableDatabase ().

Класс вызовет исключение SQLiteAssetHelperException, если вы не предоставите файл с соответствующим именем.

Методы SQLiteOpenHelper onConfigure, onCreate и onDowngrade не поддерживаются этой реализацией и были объявлены окончательными.

Изменить

Запрошена помощь, и теперь она предоставлена. Прежде всего вы должны поместить папку databases в папку assets. Имя должно быть databases, написано точно и с учетом регистра. Вот где библиотека ищет вашу базу данных. Затем вы должны поместить sqliteassethelper-2.0.1.jar в вашу libs папку. Теперь просто напишите код.

Вот пример кода этого в действии.

public class MyDatabase extends SQLiteAssetHelper {

    private static String DATABASE_NAME = "your_database_name.sqlite"; // must have file extension
    private static int DATABASE_VERSION = 1;

    public static final String SAMPLE_TABLE = "your_table_name";

    public static final String SAMPLE_COLUMN= "your_column_name";

    public MyDatabase(Context context) {
        super(context, DATABASE_NAME, null, DATABASE_VERSION);
    }

    /**
     * Returns a sample string.
     * @return sample - a sample string from your database table
     */
    public String getSample() {
        SQLiteDatabase database = this.getReadableDatabase();
        String[] columnsArray = { SAMPLE_COLUMN };
        Cursor cursor = database.query(true, SAMPLE_TABLE, columnsArray, null, null, null, null, null, null);
        if (cursor != null) {
            cursor.moveToFirst();
        }

        String sample = cursor.getString(cursor.getColumnIndex(SAMPLE_COLUMN));

        cursor.close();
        database.close();

        return sample;
    }
}

Затем вы вызываете это в своем приложении следующим образом

public void databaseTest() {
    MyDatabase db = new MyDatabase(YourActivity.this);
    String sample = db.getSample();
    Log.v("SAMPLE", sample);
    db.close();
}

Если вам что-то еще нужно, дайте мне знать

person Jawascript    schedule 23.09.2014
comment
Я создал класс, расширяющий SQLiteAssetHelper, и добавил метод для создания базы данных в папке data / data /.../ databases. вот он: public void testCreation () {SQLiteDatabase db = getReadableDatabase (); } но при вызове - я получаю исключение: android.database.sqlite.SQLiteCantOpenDatabaseException: неизвестная ошибка (код 14): не удалось открыть базу данных. Я попытался поместить файл базы данных непосредственно в папку с ресурсами, а затем в папку с активами / базами данных - по-прежнему нет удача. Помощь?... - person amazingwolf; 26.09.2014
comment
Я скопировал это и поместил соответствующее имя таблицы и столбец, и теперь получаю другую ошибку (ура?): 'Android.database.sqlite.SQLiteCantOpenDatabaseException: неизвестная ошибка (код 14): не удалось открыть базу данных' Я сейчас ищу в Google - моя база данных открывается в браузере sqlite, поэтому она не прерывается. - person amazingwolf; 28.09.2014
comment
@amazingwolf Три вещи. Во-первых, убедитесь, что ваша база данных находится в папке баз данных. Во-вторых, полностью удалите приложение с эмулятора и / или устройства. Затем снова запустите вашу программу. В-третьих, обновите свой код вверху, указав сообщение logcat, чтобы я мог видеть новый код. - person Jawascript; 29.09.2014
comment
@amazingwolf без проблем, рад, что он работает, примите ответ, когда у вас будет возможность - person Jawascript; 29.09.2014
comment
На самом деле теперь у меня другая ошибка, я опубликовал logcat как правку в вопросе ... Я новичок в этом, большое спасибо за ваше терпение. Если вы можете помочь, это было бы здорово, я погуглил, и все, что я нашел, это ссылки на отсутствующее расширение файла db, что не относится к моему коду. - person amazingwolf; 01.10.2014
comment
@amazingwolf 2 вещи: 1 принимаю исходный ответ, 2 задаю новый вопрос, поскольку исходный вопрос был решен. Это помогает всем, кто заходит на этот сайт. Спасибо - person Jawascript; 01.10.2014

Хорошо, я наконец-то заставил его работать с кодом, который я разместил изначально. Решение не требовало использования SQLiteAssetHelper (хотя оно действительно сокращает объем кода, который необходимо написать - спасибо @javascript за ВАШУ БОЛЬШУЮ ПОМОЩЬ). Я взял старую базу данных sqlite, которая у меня была, и запустил с ней приложение - при чтении она не вызвала никаких исключений. Хотя мой исходный файл был открыт во всех инструментах управления sqlite (чтобы не было повреждений), я решил создать новый файл базы данных.

Разница между старой базой данных, которую я использовал для тестирования кода, и базой данных приложения заключалась (за исключением схемы) в расширении файла. Старое расширение базы данных было «.db», а база данных приложения - «.sqlite». Итак, я создал новую базу данных С РАСШИРЕНИЕМ ".db", создал в ней все таблицы и - вуаля - приложению удалось скопировать файл из папки ресурсов в папку баз данных в эмуляторе.

Надеюсь, это кому-то поможет, это было очень неприятно :)

person amazingwolf    schedule 30.09.2014