我从资产中复制的 sqlite 数据库不包含任何 table,但如果我使用硬编码路径,它会在其他设备上不起作用

My copied sqlite database from assets does not contain any table, but if I use hardcoded path it does which does not work on other devices

它通过使用硬编码路径工作。我的路径错了吗?这是我获取路径的代码

public class DatabaseAssets extends SQLiteOpenHelper {
    private static String dbName = "questions.db";
    Context context;
    File dbFile;
    public DatabaseAssets(Context context) {
        super(context, "questions.db", null, 1);
        this.context = context;
        File DB_PATH = context.getDatabasePath(dbName);
        String db = DB_PATH.getAbsolutePath();
        dbFile= new File(db + "questions.db");
    }

这是我的 getWritableDatabase 和 getReadableDatabase 代码。

    @Override
    public synchronized SQLiteDatabase getWritableDatabase() {
        if(!dbFile.exists()){
        SQLiteDatabase db = super.getWritableDatabase();
        copyDataBase(db.getPath());
        }
        return super.getWritableDatabase();
    }
    @Override
    public synchronized SQLiteDatabase getReadableDatabase() {
        if(!dbFile.exists()){
        SQLiteDatabase db = super.getReadableDatabase();
        copyDataBase(db.getPath());
        }
    return super.getReadableDatabase();
    }

这是我的复制代码,但我不认为这是问题所在。

    private void copyDataBase(String dbPath){
       try{
           InputStream assestDB = context.getAssets().open("databases/"+"questions.db");
           OutputStream appDB = new FileOutputStream(dbPath,false);
           byte[] buffer = new byte[1024];
           int length;
           while ((length = assestDB.read(buffer)) > 0) {
               appDB.write(buffer, 0, length);
           }
           appDB.flush();
           appDB.close();
           assestDB.close();
       }catch(IOException e){
           e.printStackTrace();
           }
    }
}

我认为您的问题是因为您使用的是 SQLiteDatabase db = super.getReadableDatabase(); 或 getWritableDatabase(无关紧要),而且您将数据库名称附加到数据库名称(因此路径将是 data/data//databases/questions.dbquestions.db 而不是 data/data//databases/questions.db) :-

您可以使用以下任一方式解决路径名问题:-

public DatabaseAssets(Context context) {
    super(context, "questions.db", null, 1);
    this.context = context;
    File DB_PATH = context.getDatabasePath(dbName);
    String db = DB_PATH.getAbsolutePath();
    //dbFile = new File(db + "questions.db");
    // path will be data/data/<the_package_name>/database/questions.dbquestions.db
    dbFile = new File(db);
}

或 :-

public DatabaseAssets(Context context) {
    super(context,dbName,null,1);
    this.context = context;
    dbFile = context.getDatabasePath(dbName);
}

关于第一个提到的问题,历史上一直使用获取可写或可读数据库来规避资产副本失败的问题(由于数据库目录不存在时安装了一个应用程序)。它所做的是创建一个数据库,然后重写它以创建数据库目录。

在 Android 的更高版本中,默认模式现在是 WAL(预写日志记录),它以前是 Journal。使用 WAL,对数据库的更改将写入 WAL 文件。复制资产只会复制数据库而不是 WAL 文件。数据库打开检测到问题(WAL 部分与数据库不符)并且过于亲切 Android API 创建一个新的空数据库而不是失败。

为避免此问题,您可以创建丢失的目录(无论如何实际上效率更高)。所以不是 :-

if(!dbFile.exists()){
    SQLiteDatabase db = super.getWritableDatabase();
    copyDataBase(db.getPath());
}

使用:-

if(!dbFile.exists()){
    if (!dbFile.getParentFile().exists) {
        dbfile.mkdirs()
    }
    copyDataBase(dbFile.getPath());
}