Android Studio Room:无法创建数据库文件

Android Studio Room: Unable to create database file

我正在尝试使用 Room(使用 Java)在 Android Studio 中创建一个简单的数据库。这是另一个应用程序中的插件,因此没有默认 Activity。当我转到 Device File Explorer 时,数据库文件不存在。想知道是否有人可以指出正确的方向?

这是我创建数据库的代码:

public abstract class AppDatabase extends RoomDatabase {

    public abstract NoteDao NoteDao();

    private static AppDatabase noteDB;

    public static AppDatabase getInstance(Context context) {
        if (null == noteDB) {
            noteDB = buildDatabaseInstance(context);
        }
        return noteDB;
    }

    private static AppDatabase buildDatabaseInstance(Context context) {
        return Room.databaseBuilder(context,
                AppDatabase.class,
                "exampleDB")
                .allowMainThreadQueries().build();
    }

    public void cleanUp(){
        noteDB = null;
    }

}

这是我的 DAO 代码:

@Dao
public interface NoteDao {

    @Query("SELECT * FROM note")
    List<Note> getAll();

    @Insert
    void insert(Note note);

    @Delete
    void delete(Note note);

    @Update
    void update(Note note);

这是我的实体代码:

@Entity(tableName = "note")
public class Note {

    @PrimaryKey(autoGenerate = true)
    public int id;

    @ColumnInfo(name = "name")
    public String name;


    public Note(String name){
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Note)) return false;

        Note note = (Note) o;

        if (id != note.id) return false;
        return name != null ? name.equals(note.name) : note.name == null;
    }



    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        return result;
    }

我在插件入口点的 onCreate 方法中创建数据库。但是,当我使用下面的函数检查数据库是否存在时,它总是 returns False.

    private static String doesDatabaseExist(Context context, String dbName) {
        File dbFile = context.getDatabasePath(dbName);
        String path = dbFile.getAbsolutePath();
        return dbFile.exists();
    }

当我进入 Device File Explorer 并搜索应该创建数据库文件的路径时,它不存在。它也不会出现在数据库检查器中。我浏览了很多教程,但没有发现我的代码有任何问题。 Gradle 文件和依赖项都是正确的(我遵循了 Android 开发人员指南)并且还在清单中添加了权限。

谢谢!

When I go into Device File Explorer and and search for the path where the database file should have been created, it does not exist.

可能的原因(无法肯定地说)是您没有访问数据库(例如,使用其中一个 dao 添加内容或尝试检索内容)。这是因为只有在尝试打开数据库时才真正创建数据库。

与其使用 Dao(例如 NoteDao.getAll()),不如使用(例如)强制打开:-

private static AppDatabase buildDatabaseInstance(Context context) {

    AppDatabase db = Room.databaseBuilder(context,
            AppDatabase.class,
            "exampleDB")
            .allowMainThreadQueries().build();
    db.getOpenHelper().getWritableDatabase(); //<<<<< Forces an Open
    return db;
}

也许考虑对您的代码进行以下调整,以便(最终)打开 2 个数据库(相同的名称以及它们是否被强制打开)。

已添加回调,以便在调用 onCreate 和 onOpen 时输出表示发生的事件已写入日志。

所以修改后的@Database class AppDatabase 是:-

@Database(entities = {Note.class},version = 1)
public abstract class AppDatabase extends RoomDatabase {

    public abstract NoteDao NoteDao();

    private static AppDatabase noteDB;
    public static final String TAG = "OPENINFO"; /* added */

    /* not used for demo */
    public static AppDatabase getInstance(Context context, String dbName, boolean forceOpen) {
        if (null == noteDB) {
            noteDB = buildDatabaseInstance(context,dbName,forceOpen);
        }
        return noteDB;
    }

    /* made public and added ability to pass dbname and also boolean to force open or not */
    public static AppDatabase buildDatabaseInstance(Context context, String dbName, boolean forceOpen) {

        AppDatabase db = Room.databaseBuilder(context,
                AppDatabase.class,
                dbName)
                /* ADDED callbacks to allow open and create to be logged */
                .addCallback(new Callback() {
                    @Override
                    public void onCreate(@NonNull SupportSQLiteDatabase db) {
                        super.onCreate(db);
                        Log.d(TAG,"OnCreate callback invoked for " + dbName);
                    }

                    @Override
                    public void onOpen(@NonNull SupportSQLiteDatabase db) {
                        super.onOpen(db);
                        Log.d(TAG,"onOpen callback invoked for " + dbName);
                    }

                    @Override
                    public void onDestructiveMigration(@NonNull SupportSQLiteDatabase db) {
                        super.onDestructiveMigration(db);
                    }
                })
                .allowMainThreadQueries().build();
        if (forceOpen) {
            db.getOpenHelper().getWritableDatabase();
        }
        return db;
    }

    /* Placed in here to keep everything together */
    /* Slightly modified and made public */
    public static String doesDatabaseExist(Context context, String dbName) {
        File dbFile = context.getDatabasePath(dbName);
        String path = dbFile.getAbsolutePath();
        return String.valueOf(dbFile.exists());
    }

    /* Will close DB if it is open */
    public void cleanUp(){
        if (noteDB != null && noteDB.isOpen()) {
            noteDB.close();
        }
        noteDB = null;
    }
}

在 activity 中使用以下代码:-

public class MainActivity extends AppCompatActivity {

    AppDatabase db1, db2;
    NoteDao dao1,dao2;
    private static final String db1Name = "example1";
    private static final String db2name = "example2";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /* First run then neither file should exist at this stage, subsequent runs they will exist */
        Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
        Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
        Log.d(AppDatabase.TAG,"Getting instance for " + db1Name + " not force opened");
        db1 = AppDatabase.buildDatabaseInstance(this,db1Name,false); /* not force open */
        Log.d(AppDatabase.TAG,"Getting instance for " + db2name + " force opened");
        db2 = AppDatabase.buildDatabaseInstance(this,db2name,true);
        Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
        Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
        dao1 = db1.NoteDao();
        dao2 = db2.NoteDao();
        Log.d(AppDatabase.TAG,"Dao retrieved for " + db1Name + " accessing db via Dao");
        dao1.getAll(); /* will open DB */
        Log.d(AppDatabase.TAG,"Dao retrieved for " + db2name + " accessing via Dao");
        dao2.getAll(); /* would open DB BUT DB already opened */
        Log.d(AppDatabase.TAG,"File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this,db1Name));
        Log.d(AppDatabase.TAG,"File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this,db2name));
    }
}

当第一个 运行 然后日志包括:-

2021-09-21 10:47:54.405  D/OPENINFO: File exists for example1 false
2021-09-21 10:47:54.405  D/OPENINFO: File exists for example2 false
2021-09-21 10:47:54.406  D/OPENINFO: Getting instance for example1 not force opened
2021-09-21 10:47:54.415  D/OPENINFO: Getting instance for example2 force opened
2021-09-21 10:47:54.443  D/OPENINFO: OnCreate callback invoked for example2
2021-09-21 10:47:54.450  D/OPENINFO: onOpen callback invoked for example2
2021-09-21 10:47:54.450  D/OPENINFO: File exists for example1 false
2021-09-21 10:47:54.450  D/OPENINFO: File exists for example2 true
2021-09-21 10:47:54.454  D/OPENINFO: Dao retrieved for example1 accessing db via Dao
2021-09-21 10:47:54.478  D/OPENINFO: OnCreate callback invoked for example1
2021-09-21 10:47:54.483  D/OPENINFO: onOpen callback invoked for example1
2021-09-21 10:47:54.487  D/OPENINFO: Dao retrieved for example2 accessing via Dao
2021-09-21 10:47:54.490  D/OPENINFO: File exists for example1 true
2021-09-21 10:47:54.490  D/OPENINFO: File exists for example2 true

可以看出,example2(强制打开)的文件在检索实例后存在,而 example1 在尝试通过 getAll().[=19 检索数据之前不存在=]

回调的输出确认相同。

从 DeviceExplorer 的角度来看,如果代码只是(仅第一个 运行):-

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    /* First run then neither file should exist at this stage, subsequent runs they will exist */
    Log.d(AppDatabase.TAG, "File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this, db1Name));
    Log.d(AppDatabase.TAG, "File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this, db2name));
    Log.d(AppDatabase.TAG, "Getting instance for " + db1Name + " not force opened");
    db1 = AppDatabase.buildDatabaseInstance(this, db1Name, false); /* not force open */
    Log.d(AppDatabase.TAG, "Getting instance for " + db2name + " force opened");
    db2 = AppDatabase.buildDatabaseInstance(this, db2name, true);
    Log.d(AppDatabase.TAG, "File exists for " + db1Name + " " + AppDatabase.doesDatabaseExist(this, db1Name));
    Log.d(AppDatabase.TAG, "File exists for " + db2name + " " + AppDatabase.doesDatabaseExist(this, db2name));
}

然后设备资源管理器显示:-

即已创建 example2 但未创建 example1

而如果来自 activity 的完整代码是 运行(仅第一个 运行)则:-

我认为您缺少数据库的注释。 添加,如下所示的这一行:

@Database(entities = Note.class,version = 1, exportSchema = false)
public abstract class AppDatabase extends RoomDatabase {
private static AppDatabase appsDatabase;

public static synchronized AppDatabase getAppsDatabase(Context context){
    if (appsDatabase == null){
        appsDatabase = Room.databaseBuilder(context,
                AppDatabase.class,
                "notes_db").build();
    }
return appsDatabase;
}
}