Получите FileNotFoundException при попытке открыть базу данных в активах с помощью Room - PullRequest
0 голосов
/ 23 марта 2020

Я решил реструктурировать свою базу данных. Также я решил использовать Room для организации сотрудничества между моей БД и моим APP, вместо использования SQL запросов и курсоров. Моя база данных находится в папке Assets в Android Studio, а также в папке assets / files на эмуляторе. Мой класс, который копирует DB из ресурсов в папку assets / files внизу.

public class DatabaseHelper extends SQLiteOpenHelper implements TypeOfTest {
    private static String DB_PATH;
    private static String DB_NAME;
    private static final int SCHEMA = 2;

    public static final String DB_OLD = "ForEastory.db";
    public static final String DB_NEW = "ForEastory2.db";

    private Context myContext;

    public DatabaseHelper(Context context, String dbName) {
        super(context, DB_NAME, null, SCHEMA);
        this.myContext = context;
        DB_NAME = dbName;
        DB_PATH = context.getFilesDir().getPath() + "/"+ DB_NAME;
        Log.d("files dir + name", DB_PATH);
    }

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

    public void create_db(){
        InputStream myInput;
        OutputStream myOutput;
        try {
            File file = new File(DB_PATH);
            if (!file.exists()) {
                this.getReadableDatabase();
                myInput = myContext.getAssets().open(DB_NAME);
                String outFileName = DB_PATH;

                myOutput = new FileOutputStream(outFileName);

                byte[] buffer = new byte[1024];
                int length;
                while ((length = myInput.read(buffer)) > 0) {
                    myOutput.write(buffer, 0, length);
                }

                myOutput.flush();
                myOutput.close();
                myInput.close();
            }
        }
        catch(IOException ex){
            ex.printStackTrace();
        }
    }

    public SQLiteDatabase open() throws SQLException {
        return SQLiteDatabase.openDatabase(DB_PATH, null, SQLiteDatabase.OPEN_READWRITE);
    }

    @Override
    public void onCreate(SQLiteDatabase db) {

    }
}

Как вы можете видеть, здесь у меня есть названия баз данных. Этот класс работы с моей старой БД. Кроме того, используя Device FIle Explorer, я вижу, что база данных на самом деле находится в папке assets / files на эмуляторе. Возможно, проблема в классе Room Database, где я пытаюсь использовать свою подготовленную базу данных.

@androidx.room.Database(entities = {Question.class, Topic.class, Test.class}, version = 1, exportSchema = false)
public abstract class Database extends RoomDatabase {
    private static Database instance;

    public abstract TestDao testDao();

    public static String ASSET_DIR;

    public static synchronized Database getInstance(Context context)  {
        if (instance == null) {
            try {
                ASSET_DIR = context.getFilesDir().getPath() + "/"+ DatabaseHelper.DB_NEW;
                instance = Room.databaseBuilder(context.getApplicationContext(),
                        Database.class,
                        "AppDB")
                        .createFromAsset(ASSET_DIR)
                        .build();
            }
            catch (Exception e) {
                e.printStackTrace();
            }
        }
        return instance;
    }
}

Вот мой класс TestDao.

@Dao
public interface TestDao {

    @Query("SELECT * FROM tests WHERE topic_id = :id")
    TopicWithQuestions getTopicWithQuestionsById(int id);
}

И как мне вообще это использовать.

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test);

        testNum = findViewById(R.id.testNum);
        getQuestions = findViewById(R.id.get_questions);

        DatabaseHelper databaseHelper = new DatabaseHelper(getApplicationContext(), DatabaseHelper.DB_NEW);
        databaseHelper.create_db();
        try {
            myDb = databaseHelper.open();
        } catch (SQLException e) {
            e.printStackTrace();
        }

        db = Database.getInstance(getApplicationContext());
        testDao = db.testDao();

        getQuestions.setOnClickListener(v -> {
            try {
                int id = Integer.parseInt(testNum.getText().toString());

                //In this place i've catched the exception (RoomTest.java:61)
                TopicWithQuestions topicWithQuestions = testDao.getTopicWithQuestionsById(id);
                List<Question> questionList = topicWithQuestions.getQuestion();

                for (Question question : questionList) {
                    System.out.println(question.getQuestion());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }

Наконец, logcat:

W/System.err: java.lang.RuntimeException: Unable to copy database file.
        at androidx.room.SQLiteCopyOpenHelper.verifyDatabaseFile(SQLiteCopyOpenHelper.java:131)
        at androidx.room.SQLiteCopyOpenHelper.getWritableDatabase(SQLiteCopyOpenHelper.java:87)
        at androidx.room.RoomDatabase.inTransaction(RoomDatabase.java:476)
        at androidx.room.RoomDatabase.assertNotSuspendingTransaction(RoomDatabase.java:281)
        at com.prodadimhaski.eastory2.Room.Dao.TestDao_Impl.getTopicWithQuestionsById(TestDao_Impl.java:33)
        at com.prodadimhaski.eastory2.Room.RoomTest.lambda$onCreate$0$RoomTest(RoomTest.java:61)
        at com.prodadimhaski.eastory2.Room.-$$Lambda$RoomTest$6iMa7yj1shnTdXRtmzsROKAwX-w.onClick(lambda)
        at android.view.View.performClick(View.java:4438)
        at android.view.View$PerformClick.run(View.java:18422)
        at android.os.Handler.handleCallback(Handler.java:733)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:136)
        at android.app.ActivityThread.main(ActivityThread.java:5001)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:515)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:785)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:601)
        at dalvik.system.NativeStart.main(Native Method)
    Caused by: java.io.FileNotFoundException: /data/data/com.prodadimhaski.eastory2/files/ForEastory2.db
        at android.content.res.AssetManager.openAsset(Native Method)
        at android.content.res.AssetManager.open(AssetManager.java:316)
        at android.content.res.AssetManager.open(AssetManager.java:290)
        at androidx.room.SQLiteCopyOpenHelper.copyDatabaseFile(SQLiteCopyOpenHelper.java:178)
        at androidx.room.SQLiteCopyOpenHelper.verifyDatabaseFile(SQLiteCopyOpenHelper.java:128)
        ... 17 more

Этот __db.assertNotSuspendingTransaction(); является фрагментом кода в TestDao_Impl. java: 33

1 Ответ

0 голосов
/ 24 марта 2020

Согласно предварительно заполненной базе данных из официальной документации . У вас есть 2 варианта:

1) createFromAssets : в этом варианте вы можете создать каталог с именем «database» в папке активов, чтобы вы могли выглядеть следующим образом:

.createFromAssets("/databases/YOUR DATABASE FILENAME")

2) createFromFile : эта опция может работать с файлом, которому вы назначаете путь.

.createFromFile(File("YOUR FILE PATH"))

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

    private fun copyDBFromStorage(databaseName: String) {
    if (checkIfDBExists(this, databaseName)) return
    val databaseFile = File(this.getDatabasePath(databaseName).toString())
    val sourceLocation = assets.open("Your database file path")
    try {
        val inputStream = sourceLocation
        val os = FileOutputStream(databaseFile)
        val buffer = ByteArray(1024 * 32)
        var length = inputStream.read(buffer)
        while (length > 0) {
            os.write(buffer, 0, length)
            length = inputStream.read(buffer)
        }
        os.flush()
        os.close()
        inputStream.close()

    } catch (ex: IOException) {
        ex.printStackTrace();
        throw  RuntimeException("Error copying storage database");
    }
}

private fun checkIfDBExists(
    context: Context,
    databaseName: String
): Boolean {
    val dbfile = File(context.getDatabasePath(databaseName).toString())
    if (dbfile.exists()) return true
    if (!dbfile.parentFile.exists()) {
        dbfile.parentFile.mkdirs()
    }
    return false
}

Примечание : вы можете преобразовать этот Kotlin код в java, используя android студийную функцию преобразования кода.

Если вы обнаружили какие-либо проблемы с этим, пожалуйста, ответьте.

Счастливое кодирование ?

...