我从资产中复制的 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());
}
它通过使用硬编码路径工作。我的路径错了吗?这是我获取路径的代码
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/
您可以使用以下任一方式解决路径名问题:-
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());
}