数据库导入和导出在 Android Pie 中不起作用
Database Import and Export not working in Android Pie
下面是导入导出SQLite数据库的工作方法。它在除 Android Pie 之外的所有 android 版本中工作正常。当我尝试导入 Android 饼图时,它显示成功,但未恢复数据库。谁能帮我解决 Android Pie(API 28).
private void importDB() {
try {
File sd = Environment.getExternalStorageDirectory();
File cur_db_pat = new File(this.getDatabasePath(DATABASE_NAME).getAbsolutePath());
if (sd.canWrite()) {
String backupDBPath = bac_dir_nam +"/" + DATABASE_NAME;
File currentDB = new File(sd, backupDBPath);
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(cur_db_pat).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getBaseContext(), cur_db_pat.toString(),
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG)
.show();
}
}
private void exportDB() {
try {
File sd = Environment.getExternalStorageDirectory();
File cur_db_pat = new File(this.getDatabasePath(DATABASE_NAME).getAbsolutePath());
if (sd.canWrite()) {
String backupDBPath = bac_dir_nam+"/" + DATABASE_NAME;
File backupDB = new File(sd, backupDBPath);
FileChannel src = new FileInputStream(cur_db_pat).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getBaseContext(), backupDB.toString(),
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG)
.show();
}
}
我对文件系统没有太多经验。所以一个例子会有很大帮助。
在 Android 中,Pie+ SQLite 已更改为默认使用通常更高效的预写日志记录 (WAL) 而不是日志模式。
因此会有两个与数据库同名但后缀为-shm(共享内存文件)和-wal[=38的文件=](预写日志),我认为它们的存在是导致问题的原因。
Temporary Files Used By SQLite (see 2.2 and 2.3)
一个修复方法是使用 SQliteDatabase disableWriteAheadLogging 方法禁用预写日志记录,之前的方法将像以前一样工作,但日志模式效率较低。
- (如果使用 SQliteOpenHelper 的子类,则覆盖 onConfigure 方法以调用此方法。)disableWriteAheadLogging.
另一个修复方法是在恢复时删除这两个文件。为避免损坏的可能性,您必须确保在进行备份之前对数据库进行了充分的检查点设置。参见 PRAGMA checkpoint;
以下是在恢复时删除这两个文件的片段(注意假设备份已通过足够的检查点进行):-
// Added for Android 9+ to delete shm and wal file if they exist
File dbshm = new File(dbfile.getPath() + "-shm");
File dbwal = new File(dbfile.getPath()+ "-wal");
if (dbshm.exists()) {
dbshm.delete();
}
if (dbwal.exists()) {
dbwal.delete();
}
另一个修复方法是另外备份并随后恢复 -shm 和 -wal 文件。
您可能还希望考虑在 importing/restoring 时重命名原始文件的潜在好处,如果复制后检查新文件(例如使用 PRAGMA integrity_check;)结果表明没有问题则删除重命名的原文件,否则删除导入的文件并将原文件重命名为原文件名,说明导入失败。
在你的 class for Db WorkHelper ovverride onOpen() 方法和设置 disableWriteAheadLogging 然后调用 onOpen() 标准,如果版本 android sdk 28 , 当然,旧版本仍然是旧模式。
@Override
public void onOpen(SQLiteDatabase database) {
super.onOpen(database);
if(Build.VERSION.SDK_INT >= 28)
{
database.disableWriteAheadLogging();
}
}
就我而言,工作完美。
与其他评论者所建议的不同,在禁用预写日志记录后,您不能依赖由单个文件组成的数据库,并且不能假设 -shl 和 -wal 文件名保持正确.这是 sqlite3 / Android 的所有实现细节,因此随时可能更改(就像旧代码损坏一样)。
我希望继续工作的一种方法是使用 sqlite3 的 .dump 命令将数据库转换为 SQL,稍后可以执行该命令以重新创建数据库。
我还没有测试过以下内容,但希望类似的东西应该可以工作:
// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0
// Untested:
private byte[] exportDatabase(SQLiteDatabase database) throws IOException {
Process process = new ProcessBuilder()
.command("/system/bin/sqlite3", database.getPath(), ".dump")
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.start();
try (InputStream inputStream = process.getInputStream()) {
// [read the full contents of inputStream and save them somewhere]
return ByteStreams.toByteArray(inputStream);
} finally {
waitForProcess(process);
}
}
private void importDatabase(String databasePath, InputStream backedUpData) throws IOException {
// restore the database:
Process process = new ProcessBuilder()
.command("/system/bin/sqlite3", databasePath)
.redirectInput(ProcessBuilder.Redirect.PIPE)
.start();
try (OutputStream outputStream = process.getOutputStream()) {
// now write the backed-up contents back to outputStream
ByteStreams.copy(backedUpData, outputStream);
}
waitForProcess(process);
}
private static void waitForProcess(Process process) {
try {
process.waitFor();
} catch (InterruptedException e) {
// ignore interruption, restore interrupt flag
Thread.currentThread().interrupt();
}
}
显然,您必须确保:
- 您正在备份的数据库当前未打开。
- 您正在恢复的数据库尚不存在。
下面是导入导出SQLite数据库的工作方法。它在除 Android Pie 之外的所有 android 版本中工作正常。当我尝试导入 Android 饼图时,它显示成功,但未恢复数据库。谁能帮我解决 Android Pie(API 28).
private void importDB() {
try {
File sd = Environment.getExternalStorageDirectory();
File cur_db_pat = new File(this.getDatabasePath(DATABASE_NAME).getAbsolutePath());
if (sd.canWrite()) {
String backupDBPath = bac_dir_nam +"/" + DATABASE_NAME;
File currentDB = new File(sd, backupDBPath);
FileChannel src = new FileInputStream(currentDB).getChannel();
FileChannel dst = new FileOutputStream(cur_db_pat).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getBaseContext(), cur_db_pat.toString(),
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG)
.show();
}
}
private void exportDB() {
try {
File sd = Environment.getExternalStorageDirectory();
File cur_db_pat = new File(this.getDatabasePath(DATABASE_NAME).getAbsolutePath());
if (sd.canWrite()) {
String backupDBPath = bac_dir_nam+"/" + DATABASE_NAME;
File backupDB = new File(sd, backupDBPath);
FileChannel src = new FileInputStream(cur_db_pat).getChannel();
FileChannel dst = new FileOutputStream(backupDB).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
Toast.makeText(getBaseContext(), backupDB.toString(),
Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.toString(), Toast.LENGTH_LONG)
.show();
}
}
我对文件系统没有太多经验。所以一个例子会有很大帮助。
在 Android 中,Pie+ SQLite 已更改为默认使用通常更高效的预写日志记录 (WAL) 而不是日志模式。
因此会有两个与数据库同名但后缀为-shm(共享内存文件)和-wal[=38的文件=](预写日志),我认为它们的存在是导致问题的原因。 Temporary Files Used By SQLite (see 2.2 and 2.3)
一个修复方法是使用 SQliteDatabase disableWriteAheadLogging 方法禁用预写日志记录,之前的方法将像以前一样工作,但日志模式效率较低。
- (如果使用 SQliteOpenHelper 的子类,则覆盖 onConfigure 方法以调用此方法。)disableWriteAheadLogging.
另一个修复方法是在恢复时删除这两个文件。为避免损坏的可能性,您必须确保在进行备份之前对数据库进行了充分的检查点设置。参见 PRAGMA checkpoint;
以下是在恢复时删除这两个文件的片段(注意假设备份已通过足够的检查点进行):-
// Added for Android 9+ to delete shm and wal file if they exist
File dbshm = new File(dbfile.getPath() + "-shm");
File dbwal = new File(dbfile.getPath()+ "-wal");
if (dbshm.exists()) {
dbshm.delete();
}
if (dbwal.exists()) {
dbwal.delete();
}
另一个修复方法是另外备份并随后恢复 -shm 和 -wal 文件。
您可能还希望考虑在 importing/restoring 时重命名原始文件的潜在好处,如果复制后检查新文件(例如使用 PRAGMA integrity_check;)结果表明没有问题则删除重命名的原文件,否则删除导入的文件并将原文件重命名为原文件名,说明导入失败。
在你的 class for Db WorkHelper ovverride onOpen() 方法和设置 disableWriteAheadLogging 然后调用 onOpen() 标准,如果版本 android sdk 28 , 当然,旧版本仍然是旧模式。
@Override
public void onOpen(SQLiteDatabase database) {
super.onOpen(database);
if(Build.VERSION.SDK_INT >= 28)
{
database.disableWriteAheadLogging();
}
}
就我而言,工作完美。
与其他评论者所建议的不同,在禁用预写日志记录后,您不能依赖由单个文件组成的数据库,并且不能假设 -shl 和 -wal 文件名保持正确.这是 sqlite3 / Android 的所有实现细节,因此随时可能更改(就像旧代码损坏一样)。
我希望继续工作的一种方法是使用 sqlite3 的 .dump 命令将数据库转换为 SQL,稍后可以执行该命令以重新创建数据库。
我还没有测试过以下内容,但希望类似的东西应该可以工作:
// Copyright 2021 Google LLC.
// SPDX-License-Identifier: Apache-2.0
// Untested:
private byte[] exportDatabase(SQLiteDatabase database) throws IOException {
Process process = new ProcessBuilder()
.command("/system/bin/sqlite3", database.getPath(), ".dump")
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.start();
try (InputStream inputStream = process.getInputStream()) {
// [read the full contents of inputStream and save them somewhere]
return ByteStreams.toByteArray(inputStream);
} finally {
waitForProcess(process);
}
}
private void importDatabase(String databasePath, InputStream backedUpData) throws IOException {
// restore the database:
Process process = new ProcessBuilder()
.command("/system/bin/sqlite3", databasePath)
.redirectInput(ProcessBuilder.Redirect.PIPE)
.start();
try (OutputStream outputStream = process.getOutputStream()) {
// now write the backed-up contents back to outputStream
ByteStreams.copy(backedUpData, outputStream);
}
waitForProcess(process);
}
private static void waitForProcess(Process process) {
try {
process.waitFor();
} catch (InterruptedException e) {
// ignore interruption, restore interrupt flag
Thread.currentThread().interrupt();
}
}
显然,您必须确保:
- 您正在备份的数据库当前未打开。
- 您正在恢复的数据库尚不存在。