В одном из своих приложений для 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, из-за чего это по какой-то причине не работает? Что может быть причиной этого?
Google В самом деле необходимо добавить поддержку API резервного копирования в свои привязки Android sqlite.
@MichaelDougan, это было действительно так! С некоторыми настройками для моего конкретного приложения теперь это работает. Спасибо, если вы опубликуете это как ответ, я могу отметить его как принятый ответ
Я не могу быть уверен, пока не увижу 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;
}
}
У меня есть пример кода на гитхабе для основных операций с базами данных в Android, где вы также можете взглянуть.
Проблема с включенным 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 являются лучшими способами: -
Все вышеперечисленное описано в Отправка приложения для Android с предварительно заполненной базой данных.
Код решения фактически включает в себя как 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;
}
Да, с Android 9 есть два дополнительных файла, которые являются частью базы данных SQLite, и если вы скопируете базу данных, перекрывающую существующую базу данных, и не удалите сначала эти два других файла, тогда база данных будет считать себя поврежденной. См. этот ответ: Отправка приложения для Android с предварительно заполненной базой данных
Но @MikeT должен получить признание, так как связанный ответ был его с самого начала!
Очень верно. Сначала его ответа еще не было, поэтому ваш комментарий помог мне решить проблему. Однако теперь я принял его как правильный ответ. Тем не менее спасибо за помощь!
Да, с Android 9 есть два дополнительных файла, которые являются частью базы данных SQLite, и если вы скопируете базу данных, перекрывающую существующую базу данных, и не удалите сначала эти два других файла, тогда база данных будет считать себя поврежденной. Смотрите этот ответ. stackoverflow.com/questions/56008907/…