在我的一个Android应用程序中,我使用了一个预填充的数据库,该数据库是在首次使用时复制的。到目前为止,这在所有Android版本上均能正常运行,但是使用API 28失败。它确实复制数据库,并且以后可以连接到数据库,但是由于某些原因,无法访问任何表。我收到表不存在的错误。
我已经通过从仿真器下载数据库来检查数据库,但是数据库没有问题。在代码中创建数据库的另一个应用程序运行良好(创建了数据库,之后可以找到表)。
我用于复制数据库的代码:
public void copyDatabase() throws IOException{
InputStream myInput = mContext.getAssets().open(mDbSource);
// Path to the just created empty db
String outFileName = getDbPath() + mDbName;
//Open the empty db as the output stream
OutputStream myOutput = new FileOutputStream(outFileName);
//transfer bytes from the inputfile to the outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myInput.read(buffer))>0){
myOutput.write(buffer, 0, length);
}
//Close the streams
myOutput.flush();
myOutput.close();
myInput.close();
}
private String getDbPath(){
return "/data/data/"+mContext.getResources().getString(R.string.package_name)+"/databases/";
}
API 28中是否有任何更改由于某种原因导致失败?可能是什么原因造成的?
答案 0 :(得分:2)
API 28启用的问题是WAL(预写日志记录)默认情况下处于打开状态。如果通常打开数据库,并且在检查数据库是否存在时经常打开数据库,则将创建两个文件-wal和-shm文件。
例如在您的代码中,这似乎表明这就是您正在做的事情:-
// Path to the just created empty db
String outFileName = getDbPath() + mDbName;
当现有数据库被副本覆盖时,-wal和-shm文件保留。
当最终尝试将数据库作为SQLiteDatabase打开时,则会引发数据库与-wal和-shm文件之间的不匹配。然后,底层的SQLiteDatabase open决定无法打开数据库,因此,为了提供可用的数据库,删除并重新创建数据库,有效地清空了数据库,从而出现了这种情况。
有三种解决方法
我建议2或3个更好的方法是:-
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;
}
答案 1 :(得分:1)
是的,对于android 9,SQLite数据库中还有两个其他文件,如果您复制覆盖现有数据库的数据库,并且不先删除其他两个文件,则该数据库将认为自身已损坏。查看此答案:Ship android app with pre populated database
但是@MikeT应该获得荣誉,因为链接的答案是他的开始!
答案 2 :(得分:0)
我不确定,直到我看到logcat。但是,我认为您在访问数据库时可能缺少一些权限。
我想问一下,您的AndroidManifest.xml
文件中是否有以下提供程序,用于授权您的应用程序访问数据库。这是sample manifest file个提供者。
<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;
}
}
我有一个sample code in Github来介绍Android中的基本数据库操作,您也可以看看。