Saya memiliki database yang disimpan di my apps folder aset dan saya copy database menggunakan kode di bawah ini ketika aplikasi pertama kali dibuka.
inputStream = mContext.getAssets().open(Utils.getDatabaseName());
if(inputStream != null) {
int mFileLength = inputStream.available();
String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();
// Save the downloaded file
output = new FileOutputStream(filePath);
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
total += count;
if(mFileLength != -1) {
// Publish the progress
publishProgress((int) (total * 100 / mFileLength));
}
output.write(data, 0, count);
}
return true;
}
Kode di atas berjalan tanpa masalah tetapi ketika anda mencoba untuk query database anda mendapatkan SQLite: Tidak ada tabel terkecuali.
Masalah ini hanya terjadi di Android P, semua versi sebelumnya dari Android bekerja dengan benar.
Ini adalah masalah yang dikenal dengan Android P atau memiliki sesuatu yang berubah?
Mengalami masalah serupa, dan soal ini menambahkan ini ke SQLiteOpenHelper
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
db.disableWriteAheadLogging();
}
Rupanya Android P set PRAGMA Log hal yang berbeda. Masih tidak tahu jika akan memiliki efek samping, tetapi tampaknya akan bekerja!
Masalah saya dengan Android P harus diselesaikan dengan menambahkan 'ini.close()' setelah ini.getReadableDatabase() di createDataBase() metode seperti di bawah ini.
private void createDataBase() throws IOException {
this.getReadableDatabase();
this.close();
try {
copyDataBase();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Masalah ini tampaknya menyebabkan kecelakaan lebih sering pada Android P dari pada versi sebelumnya, tapi itu's bukan bug pada Android P itu sendiri.
Masalahnya adalah bahwa anda garis di mana anda menetapkan nilai ke String filePath
membuka koneksi ke database yang tetap terbuka ketika anda menyalin file dari aset.
Untuk memperbaiki masalah ini, ganti baris
String filePath = mContext.getDatabasePath(Utils.getDatabaseName()).getAbsolutePath();
dengan kode untuk mendapatkan path file nilai dan kemudian menutup database:
MySQLiteOpenHelper helper = new MySQLiteOpenHelper();
SQLiteDatabase database = helper.getReadableDatabase();
String filePath = database.getPath();
database.close();
Dan juga menambahkan inner class helper:
class MySQLiteOpenHelper extends SQLiteOpenHelper {
MySQLiteOpenHelper(Context context, String databaseName) {
super(context, databaseName, null, 2);
}
@Override
public void onCreate(SQLiteDatabase db) {
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
Aku berlari ke dalam masalah yang sama. Saya menyalin database tapi tidak dari aset. Apa yang saya temukan adalah bahwa masalah ini tidak ada hubungannya dengan database saya menyalin file kode sama sekali. Itu juga tidak harus dilakukan dengan file yang dibiarkan terbuka, tidak tertutup, flushing atau sinkronisasi. Kode saya biasanya akan menimpa yang sudah ada unopen database. Apa yang tampaknya baru/karakteristik yang berbeda dengan Android Pie dan berbeda dari rilis sebelumnya dari Android, adalah bahwa ketika Android Pie menciptakan sebuah database SQLite, set journal_mode untuk WAL (write-ahead log), secara default. I've tidak pernah digunakan WAL mode dan SQLite docs mengatakan bahwa journal_mode harus MENGHAPUS secara default. Masalahnya adalah jika saya menimpa file database, let's menyebutnya saya.db, write-ahead log, saya.db-wal, masih ada dan secara efektif "memaksa" apa's yang baru disalin saya.db file. Ketika aku membuka database, sqlite_master meja biasanya hanya berisi satu baris untuk android_metadata. Semua tabel saya mengharapkan yang hilang. Solusi saya adalah untuk hanya mengatur journal_mode kembali untuk MENGHAPUS setelah membuka database, terutama ketika membuat sebuah database baru dengan Android Pie.
PRAGMA journal_mode=HAPUS;
Mungkin WAL lebih baik dan ada's mungkin beberapa cara untuk menutup database sehingga write-ahead log doesn't mendapatkan di jalan tapi aku don't benar-benar membutuhkan WAL dan modern't diperlukan untuk semua versi Android sebelumnya.
Sayangnya, jawaban yang diterima hanya "kebetulan bekerja" dalam kasus-kasus konkret, tapi itu doesn't memberikan secara konsisten bekerja saran untuk menghindari kesalahan seperti itu di Android 9.
Di sini adalah:
Setelah memanggil close(), tidak hanya semua koneksi ke database ditutup, tapi tambahan database log file yang memerah utama .sqlite file dan dihapus. Jadi, anda memiliki satu database.sqlite file saja, siap untuk ditulis atau disalin.
Jawaban ini membantu saya untuk mengetahui bahwa: https://stackoverflow.com/a/35648781/297710
Aku punya masalah ini di Android 9 di AndStatus aplikasi https://github.com/andstatus/andstatus yang cukup besar suite tes otomatis yang secara konsisten direproduksi "SQLiteException: tidak ada tabel" di Android 9 emulator sebelum melakukan ini: https://github.com/andstatus/andstatus/commit/1e3ca0eee8c9fbb8f6326b72dc4c393143a70538 Jadi jika anda're benar-benar penasaran, anda dapat menjalankan Semua tes sebelum dan setelah melakukan ini untuk melihat perbedaan.
Solusi tanpa menonaktifkan WAL
Android 9 memperkenalkan modus khusus SQLiteDatabase disebut Kompatibilitas WAL (menulis-depan loggin) yang memungkinkan sebuah database menggunakan "journal_mode=WAL" sambil menjaga perilaku menjaga maksimal satu koneksi per database.
Dalam Detail berikut: https://source.android.com/devices/tech/perf/compatibility-wal
SQLite WAL mode ini dijelaskan dalam detail berikut: https://www.sqlite.org/wal.html
Sebagai resmi docs WAL mode menambahkan kedua file database menelepon databasename dan "-wal". Jadi jika database anda bernama "data.db" hal ini disebut "data-wal.db" dalam direktori yang sama.
Solusinya adalah sekarang untuk save dan restore BAIK file (data.db dan data-wal.db) pada Android 9.
Setelah itu bekerja seperti di versi sebelumnya.
Masalah serupa, hanya Android P perangkat yang terkena dampak. Semua versi sebelumnya tidak ada masalah.
Dimatikan auto restore pada Android 9 perangkat.
Kami melakukan ini untuk memecahkan masalah. Tidak akan merekomendasikan untuk kasus produksi.
Auto pemulihan menempatkan salinan dari file database dalam direktori data sebelum copy database fungsi yang disebut dalam database helper. Oleh karena itu file.ada() mengembalikan true.
Database yang didukung dari pengembangan perangkat hilang meja. Oleh karena itu "tabel tidak ditemukan" pada kenyataannya benar.
Pertama, terima kasih untuk posting pertanyaan ini. Aku punya hal yang sama terjadi. Semua bekerja dengan baik, tapi kemudian ketika pengujian terhadap Android P Preview saya mendapatkan crash. Berikut ini's bug yang saya temukan kode ini:
private void copyDatabase(File dbFile, String db_name) throws IOException{
InputStream is = null;
OutputStream os = null;
SQLiteDatabase db = context.openOrCreateDatabase(db_name, Context.MODE_PRIVATE, null);
db.close();
try {
is = context.getAssets().open(db_name);
os = new FileOutputStream(dbFile);
byte[] buffer = new byte[1024];
while (is.read(buffer) > 0) {
os.write(buffer);
}
} catch (IOException e) {
e.printStackTrace();
throw(e);
} finally {
try {
if (os != null) os.close();
if (is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Masalah yang saya hadapi adalah kode ini bekerja baik-baik saja TAPI di SDK 28+ openOrCreateDatabase tidak lagi secara otomatis menciptakan android_metadata meja untuk anda. Jadi jika anda melakukan query dari "select * from TABEL" itu tidak akan menemukan MEJA itu karena permintaan mulai terlihat setelah "lebih" tabel yang harus metadata meja. Aku tetap ini secara manual menambahkan android_metadata meja dan semua itu baik. Berharap orang lain yang menemukan ini berguna. Butuh waktu lama untuk mencari tahu karena pertanyaan spesifik masih bekerja dengan baik.
Aku punya hal yang sama saya memiliki sebuah aplikasi di versi 4 dari android, dan ketika memperbarui ponsel saya yang memiliki android 9, kemudian saya 2 hari berusaha untuk menemukan kesalahan, terima kasih untuk komentar-komentar dalam kasus saya, saya hanya harus menambahkan ini.close ();
private void createDataBase () throws IOException {
this.getReadableDatabase ();
this.close ();
try {
copyDataBase ();
} catch (IOException e) {
throw new RuntimeException (e);
}
}
siap untuk menjalankan semua versi !!
Berikut ini's solusi sempurna untuk masalah ini:
Hanya mengganti metode ini dalam SQLiteOpenHelper
kelas:
@Override
public void onOpen(SQLiteDatabase db) {
super.onOpen(db);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
db.disableWriteAheadLogging();
}
}
Saya tidak bisa mengomentari jawaban yang diterima, jadi saya harus membuka sebuah jawaban baru.
mContext.getDatabasePath() TIDAK membuka koneksi database, itu doesn't bahkan memerlukan yang ada nama file untuk berhasil (lihat sources/android-28/android/app/ContextImpl.java):
@Override
public File getDatabasePath(String name) {
File dir;
File f;
if (name.charAt(0) == File.separatorChar) {
// snip
} else {
dir = getDatabasesDir();
f = makeFilename(dir, name);
}
return f;
}
private File makeFilename(File base, String name) {
if (name.indexOf(File.separatorChar) < 0) {
return new File(base, name);
}
throw new IllegalArgumentException(
"File " + name + " contains a path separator");
}
Dalam versi P, perubahan utama adalah WAL (Menulis Ahead Log). Berikut dua langkah yang diperlukan.
private void createDataBase() throws IOException {
jika (android.os.Membangun.VERSI.SDK_INT < android.os.Membangun.VERSION_CODES.P) { ini.getWritableDatabase(); try { copyDataBase(); } catch (IOException e) { throw new RuntimeException(e); } } }
Tampaknya bahwa anda don't menutup output stream. Sementara itu mungkin tidak menjelaskan mengapa db adalah benar-benar tidak dibuat (kecuali Android P menambahkan multi MB buffer) itu adalah praktik yang baik untuk menggunakan coba-dengan-sumber daya, seperti :
// garantees that the data are flushed and the resources freed
try (FileOutputStream output = new FileOutputStream(filePath)) {
byte data[] = new byte[1024];
long total = 0;
int count;
while ((count = inputStream.read(data)) != -1) {
total += count;
if (mFileLength != -1) {
// Publish the progress
publishProgress((int) (total * 100 / mFileLength));
}
output.write(data, 0, count);
}
// maybe a bit overkill
output.getFD().sync();
}