Когда я открываю файл базы данных в браузере БД, он пуст. И я получаю этот E / SQLiteLog: (283) восстановил 16 кадров из файла WAL. Правильно ли использовать Room для сохранения данных и заполнения их обратно, когда приложение закрыто, и наоборот. Буду рад, если кто-то может помочь, спасибо.

/ это мой экземпляр в классе db /

public static synchronized NoteDataBase getInstance(Context context){

    if (instance == null) {
        instance = Room.databaseBuilder(context.getApplicationContext(),
                NoteDataBase.class , "note_database")
                .fallbackToDestructiveMigration()
                .build();
    }
    return instance;
}
} 
@Override
public void close() {
    super.close();
    instance = null;
}

public void backup(Context context) {
    instance.close();
    //......... backup the file
    getInstance(context);
}

/ и мой репозиторий /

 public NoteRepository(Application application) {
    dataBase = NoteDataBase.getInstance(application);
    noteDao = dataBase.noteDao();
    allNotes = noteDao.getAllNotes();

}

/ некоторые из моих моделей просмотра /

public class NoteViewModel extends AndroidViewModel  {

public NoteViewModel(@NonNull Application application)  {
    super(application);
    repository = new NoteRepository(application);
    allNotes = repository.getAllNotes();
}

public void insert(Note note) {
    repository.insert(note);
}

}

/ И мой фрагмент, где я сохраняю некоторые результаты /

private void saveNote(){

    String savedscore = finalScore.getText().toString();
    ArrayList<String> daycheks =  new ArrayList<>();
    for (int i = 0; i < itemList.size(); i++) {
        daycheks.add(itemList.get(i).toString());
    }

    Note note = new Note(savedscore , daycheks);
    model.insert(note);

    Toast.makeText(getContext() , "Result saved" ,Toast.LENGTH_SHORT).show();

}
1
Radostin Rachev 20 Окт 2019 в 18:29

2 ответа

При резервном копировании базы данных вы должны либо полностью проверить базу данных (закрытие базы данных - один из вариантов (самый простой)) перед резервным копированием, либо сделать резервную копию -wal и -shm файлы вместе с фактическим файлом базы данных.

  • файлы -wal / -shm или -journal имеют то же имя, что и файл базы данных с суффиксом с соответствующим расширением.

Другой вариант - открыть базу данных в режиме журнала с помощью setJournalMode.

  • Разница между WAL и режимом журнала заключается в

    • что в режиме WAL изменения применяются к файлу -wal , а не к самой базе данных (хотя доступ к файлу -wal осуществляется так, как будто он является частью базу данных), откат сводится к удалению соответствующих данных из файла -wal .

    • В то время как в режиме журнала база данных изменяется, и эти изменения записываются в файл -journal , откат отменяет изменения в соответствии с файлом журнала.

    • Более подробную информацию см. в журнале предзаписи.

Как видно, если вы копируете базу данных без контрольной точки и без файла -wal , данные могут отсутствовать.

Примере

Следующий пример имитирует резервное копирование базы данных, включая закрытие базы данных (но пропускает фактическое резервное копирование) после добавления нескольких строк. Обратите внимание, что для удобства это делается в основном потоке.

Класс DB: -

@androidx.room.Database(entities = MyTable.class,version = 1)
public abstract class NoteDataBase extends RoomDatabase {

    private static String DBNAME = "mydb";
    static NoteDataBase instance;

    abstract MyTableDao getMyTableDao();
    public static synchronized NoteDataBase getInstance(Context context) {
        if (instance == null) {
            instance = Room.databaseBuilder(context, NoteDataBase.class,DBNAME)
                    .fallbackToDestructiveMigration()
                    .allowMainThreadQueries()
                    .build();
        }
        return instance;
    }

    @Override
    public void close() {
        super.close();
        instance = null;
    }

    public void backup(Context context) {
        instance.close();
        //......... backup the file
        getInstance(context);
    }
}
  • то есть в основном ваш класс, но, что важно, с методом резервного копирования, который закрывает базу данных, а затем устанавливает переменную экземпляра в значение null, эффективно вызывая открытие при следующем использовании.

Действие, в котором используется указанное выше: -

public class MainActivity extends AppCompatActivity {

    NoteDataBase mDB;
    MyTableDao mMyTableDao;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.d("APPINFO","Retrieving the Database Instance");
        mDB = NoteDataBase.getInstance(this);
        Log.d("APPINFO","Retrieving the Database DAO");
        mMyTableDao = mDB.getMyTableDao();
        Log.d("APPINFO","Inserting Data (10 rows)");
        for (int i=1;i < 11; i++) {
            mMyTableDao.insertMyTableRow(new MyTable("MYVALUE" + i));
        }
        Log.d("APPINFO","Mock backup/close");
        mDB.backup(this);
        Log.d("APPINFO","Retrieveing the Database Instance after the close");
        mDB = NoteDataBase.getInstance(this);
        Log.d("APPINFO","Retrieving Rows from the Database");
        for(MyTable m: mDB.getMyTableDao().getAllMyTableRows()) {
            Log.d("MYTABLEINFO","ID = " + m.getId() + " VALUE = " + m.getValue());
        }
        Log.d("APPINFO","Closing the database");
        mDB.close();
        MyTable m = mDB.getMyTableDao().getMyTableRowById(1L);
        Log.d("MYTABLEINFO","ID = " + m.getId() + " VALUE = " + m.getValue());
    }
}

Результат (журнал): -

2019-10-21 15:47:58.689 16269-16269/? D/APPINFO: Retrieving the Database Instance
2019-10-21 15:47:58.699 16269-16269/? D/APPINFO: Retrieving the Database DAO
2019-10-21 15:47:58.701 16269-16269/? D/APPINFO: Inserting Data (10 rows)
2019-10-21 15:47:58.743 16269-16269/? D/APPINFO: Mock backup/close
2019-10-21 15:47:58.752 16269-16269/? D/APPINFO: Retrieveing the Database Instance after the close
2019-10-21 15:47:58.752 16269-16269/? D/APPINFO: Retrieving Rows from the Database
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 1 VALUE = MYVALUE1
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 2 VALUE = MYVALUE2
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 3 VALUE = MYVALUE3
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 4 VALUE = MYVALUE4
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 5 VALUE = MYVALUE5
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 6 VALUE = MYVALUE6
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 7 VALUE = MYVALUE7
2019-10-21 15:47:58.773 16269-16269/? D/MYTABLEINFO: ID = 8 VALUE = MYVALUE8
2019-10-21 15:47:58.774 16269-16269/? D/MYTABLEINFO: ID = 9 VALUE = MYVALUE9
2019-10-21 15:47:58.774 16269-16269/? D/MYTABLEINFO: ID = 10 VALUE = MYVALUE10
2019-10-21 15:47:58.774 16269-16269/? D/APPINFO: Closing the database
2019-10-21 15:47:58.784 16269-16269/? E/ROOM: Invalidation tracker is initialized twice :/.
2019-10-21 15:47:58.784 16269-16269/? D/MYTABLEINFO: ID = 1 VALUE = MYVALUE1
  • Обратите внимание на предпоследнюю строку, это потому, что старый экземпляр использовался вместо получения нового экземпляра (на самом деле должен был использоваться mDB = NoteDataBase.getInstance(this);) .

Следует отметить, что база данных после выполнения вышеуказанного имеет пустой файл -wal : -

enter image description here

  • Таким образом, база данных полностью проверена.

Копирование этого файла из проводника устройства и открытие в инструменте SQLite (Navicat) показывает: -

enter image description here

  • т.е. вставленные данные существуют, как и таблица room_master_table и таблица android_metadata

Альтернатива закрытию

Альтернативой закрытию базы данных было бы использование PRAGMA wal_checkpoint (?)

Если класс БД включал следующие методы: -

public void checkpoint() {
    int attempts = 0;
    int max_attempts = 10;
    Cursor csr;
    SupportSQLiteDatabase ssd  = instance.getOpenHelper().getWritableDatabase();
    if (isWALOn(ssd)) {
        Log.d("CHKPOINTINFO","Attempt " +  (attempts + 1));
        while (checkpoint(ssd) > 0 && attempts++ < max_attempts) { }
    }
}

private boolean isWALOn(SupportSQLiteDatabase db) {
    boolean rv = false;
    Cursor csr = db.query("PRAGMA journal_mode");
    if  (csr.moveToFirst()) {
        if (csr.getString(0).toLowerCase().equals("wal")) rv = true;
    }
    csr.close();
    return rv;
}

private int checkpoint(SupportSQLiteDatabase db) {
    Log.d("CHKPOINTINFO","Attempting Database Ceckpoint");
    int blocked = 0;
    int pages_to_checkpoint = 0;
    int checkpointed_pages = 0;
    Cursor csr = db.query("PRAGMA wal_checkpoint(TRUNCATE)");
    if (csr.moveToFirst()) {
        blocked = csr.getInt(0);
        pages_to_checkpoint = csr.getInt(1);
        checkpointed_pages = csr.getInt(2);
    }
    csr.close();
    Log.d("CHKPOINTINFO",
            "Checkpoint values Blocked = " + blocked +
                    " Pages to Checkpoint = " + pages_to_checkpoint +
                    " Pages Checkpointed = " + checkpointed_pages
    );
    if (blocked > 0) return -1;
    if (checkpointed_pages < pages_to_checkpoint) return 1;
    return 0;
}
  • note Примечание: Для тестирования добавлено ведение журнала, обычно оно удаляется.

Вместо использования mDB.backup() можно использовать mDB.checkpoint(). Преимущество этого в том, что база данных остается открытой.

0
MikeT 21 Окт 2019 в 11:01
Я делаю следующее, но не получаю результата. Может быть, меня что-то неправильно поняли. Вношу некоторые правки в вопрос.
 – 
Radostin Rachev
21 Окт 2019 в 21:05

Мое решение - закрыть базу данных в методе onCleared моей основной модели ViewModel, после чего журнал ошибок исчез.

class MainActivityViewModel @Inject constructor(private val reviewRepository: ReviewRepository,
                                            private val favoriteRepository: FavoriteRepository,
                                            private val firebaseService: FirebaseService,
                                            private val sharedPreferences: SharedPreferences,
                                            private val toiletDb: ToiletDb)
: ViewModel() {

// Close database when the ViewModel is cleared
override fun onCleared() {
    super.onCleared()
    toiletDb.close()
}
0
ping li 25 Янв 2020 в 05:57