3 回答

TA贡献1847条经验 获得超7个赞
API 28 开启的问题是默认情况下 WAL(预写日志记录)是开启的。通常情况下,如果打开数据库(通常在检查数据库是否存在时完成),则会创建两个文件 -wal 和 -shm 文件。
例如,在您的代码中,这似乎表明这就是您正在做的事情:-
// Path to the just created empty db String outFileName = getDbPath() + mDbName;
当现在存在的数据库被副本覆盖时,-wal 和 -shm 文件仍然存在。
当最终尝试将数据库作为 SQLiteDatabase 打开时,会引发数据库与 -wal 和 -shm 文件之间的不匹配。底层的 SQLiteDatabase open 然后决定无法打开数据库,因此,为了提供可用的数据库,删除并重新创建数据库有效地清空数据库,因此出现了这种情况。
有三种方法可以解决这个问题
覆盖onConfigure方法并强制它使用旧的日志模式(使用 [disableWriteAheadLogging](https://developer.android.com/reference/android/arch/persistence/db/SupportSQLiteDatabase#disablewriteaheadlogging方法))。
使用 SQLiteDatabase open 方法检查数据库是否存在,但要检查文件是否存在。
作为复制的一部分,删除 -wal 和 -shm 文件。
我建议 2 或 3 是更好的方法:-
没有不必要地打开(和创建)数据库的开销,只是检查它是否存在并创建目录。
由于WAL优于 Journal 日志记录,该应用程序可能会得到改进。
以上全部包含在Ship android app with pre populated database中。
解决方案代码实际上合并了 2 和 3(尽管不需要修复 3(因为修复 2 不会将数据库作为 SQLiteDatabase 打开))。
说我相信以下用于检查数据库是否存在的代码(修复 2)比上面答案中的等效代码更好:-
/**
* Check if the database already exists. NOTE will create the databases folder is it doesn't exist
* @return true if it exists, false if it doesn't
*/
public static boolean checkDataBase(Context context, String dbname) {
File db = new File(context.getDatabasePath(dbname).getPath()); //Get the file name of the database
Log.d("DBPATH","DB Path is " + db.getPath()); //TODO remove if publish App
if (db.exists()) return true; // If it exists then return doing nothing
// Get the parent (directory in which the database file would be)
File dbdir = db.getParentFile();
// If the directory does not exits then make the directory (and higher level directories)
if (!dbdir.exists()) {
db.getParentFile().mkdirs();
dbdir.mkdirs();
}
return false;
}

TA贡献2019条经验 获得超9个赞
在看到 logcat 之前我无法确定。但是,我认为您在访问数据库时可能缺少某些权限。
我想问一下检查您的AndroidManifest.xml文件中是否有以下提供程序以授予您的应用程序访问数据库的权限。这是一个包含提供商的示例清单文件。
<provider
android:name=".DatabaseContentProvider"
android:authorities="your.application.package.name"
android:exported="false" />
你需要一个DatabaseContentProvider像下面这样的类。
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public class DatabaseContentProvider extends ContentProvider {
@Nullable
@Override
public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
return null;
}
@Nullable
@Override
public Cursor query(@NonNull Uri uri,
@Nullable String[] projection,
@Nullable String selection,
@Nullable String[] selectionArgs,
@Nullable String sortOrder) {
return null;
}
@Override
public boolean onCreate() {
return true;
}
@Override
public int update(@NonNull Uri uri, @Nullable ContentValues values,
@Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Override
public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
return 0;
}
@Nullable
@Override
public String getType(@NonNull Uri uri) {
return null;
}
}
我在 Github 中有一个示例代码,用于 Android 中的基本数据库操作,您也可以在其中查看。

TA贡献1111条经验 获得超0个赞
是的,对于 android 9,SQLite 数据库中有两个附加文件,如果您复制覆盖现有数据库的数据库,并且不先删除这两个文件,那么数据库将认为自己已损坏。请参阅此答案:Ship android app with pre populated database
但是@MikeT 应该得到赞扬,因为链接的答案是他开始的!!
添加回答
举报