首页 > 解决方案 > 使用 Room 的 createFromAsset() 在数据库文件之间切换会返回空数据库

问题描述

我有一个spinner用来在SQLite数据库文件之间切换的。在spinner选择侦听器上,我将相关的数据库文件名传递给 Room 的 Database 类。

在通过调用 Room's 在数据库之间切换之前createFromAsset(),我删除了 Room 的数据库文件以避免之前数据库中的数据缓存。

我的问题是,每当我切换到另一个spinner值时,数据库什么都不返回。从我手机上的应用程序数据中读取数据库文件后,数据库文件就在那里,但表中没有条目。

这是微调器回调:

spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    @Override
    public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
        if (mActivateSpinner) {
            
            String newDatabaseName;
            
            switch (position) {
                case 0:
                    newDatabaseName = "database1.db";
                    break;
                case 1:
                    newDatabaseName = "database2.db";
                    break;

                default:
                    newDatabaseName = "database1.db";
            }

            mViewModel.deleteDatabase(newDatabaseName, () -> runOnUiThread(() -> {                  
                
                //
                // Reading database here returns empty data
                //
                
            }));
        }
    }

    @Override
    public void onNothingSelected(AdapterView<?> parent) {
    }
});

ViewModel 相关方法

public class MyViewModel extends AndroidViewModel {

    ....
    
    MyRepository mRepository;
    
    public void deleteDatabase(String newDatabaseName, OnCompletionListener listener) {
        MyRepository.resetInstance();
        MyDataBase.resetInstance();
        
        // delete current Room database file
        deleteDatabaseFile(getApplication().getApplicationContext(), "MyDataBaseFile.db", () -> initRepository(newDatabaseName, listener));
    }


    public void initRepository(String newDatabaseName, OnCompletionListener listener) {
        mRepository = MyRepository.getInstance(getApplication(), newDatabaseName, listener);
    }
    
    
    
    public static void deleteDatabaseFile(Context context, String fileName, OnCompletionListener deleteListener) {
        new Thread(() -> {
            File parent = new File(context.getApplicationInfo().dataDir + "/databases");
            File db = new File(parent, fileName);

            if (db.delete()) {
                deleteListener.onComplete();
                Log.d("TAG", "Database deleted");
            } else 
                Log.d("TAG", "Failed to delete database");
            
        }).start();

    }
    
}

存储库相关方法

public class MyRepository {

    ...
    
    private MyDataBaseDao mDao;
    private static MyRepository INSTANCE;
        
    public static MyRepository getInstance(Application application, String databaseName, OnCompletionListener listener) {
        if (INSTANCE == null) {
            INSTANCE = new MyRepository(application, databaseName, listener);
        }
        return INSTANCE;
    }
    
    private MyRepository(Application application, String databaseName, OnCompletionListener listener) {
        mDao = MyDataBase.getInstance(application.getApplicationContext(), databaseName, listener).getDao();
    }
    
}

房间数据库

@Database(entities = {MyTable.class}, version = 1, exportSchema = false)
public abstract class MyDataBase extends RoomDatabase {

    public static final String DATABASE_NAME = "MyDataBaseFile.db";

    private static volatile MyDataBase INSTANCE;

    private static final Object LOCK = new Object();

    public abstract MyDataBaseDao getDao();

    public static void resetInstance() {
        INSTANCE = null;
    }

    static public MyDataBase getInstance(final Context context, String databaseName, OnCompletionListener listener) {
        if (INSTANCE == null) {
            synchronized (LOCK) {
                if (INSTANCE == null) {
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            MyDataBase.class, DATABASE_NAME)
                            .createFromAsset("database/" + databaseName)
                            .build();
                            
                    if (listener != null)
                        listener.onComplete();

                }
            }
        }
        return INSTANCE;
    }

}

listener.onComplete()调用,并且数据库文件具有预期的大小,但表没有数据。

标签: androidandroid-sqliteandroid-roomandroid-room-prepackageddatabase

解决方案


Room 为每个数据库创建 3 个文件,在我的例子中我命名为 database MyDataBaseFile.db,然后 Room 创建 3 个文件并命名它们:

  • MyDataBaseFile.db
  • MyDataBaseFile.db-wal
  • MyDataBaseFile.db-shm

当我切换微调器值时,我只是删除MyDataBaseFile.db并留下了其他两个文件,而删除其他两个文件确实在表中显示了新数据

更新了删除方法

public static void deleteDatabase(Context context, String databaseName, OnCompletionListener deleteListener) {
    new Thread(() -> {
        File parent = new File(context.getApplicationInfo().dataDir + "/databases");
        if (deleteFile(parent, databaseName + "-wal")
                && deleteFile(parent, databaseName + "-shm")
                && deleteFile(parent, databaseName)) {
            deleteListener.onComplete();
            Log.d(TAG, "Database deleted");
        } else
            Log.d(TAG, "Failed to delete database");

    }).start();

}

/*
 * Returns:
 * ****   true: if the file doesn't exist or successfully deleted
 * ****   false: if the file can't be deleted
 * */
private static boolean deleteFile(File parent, String child) {
    File file;

    if (parent != null)
        file = new File(parent, child);
    else
        file = new File(child);

    if (file.exists())
        return file.delete();
    else
        return true;
}

推荐阅读