E/SQLiteLog: (283) recovered frames from WAL file

Viewed 3908

I get an error every time I launch my application.

E/SQLiteLog: (283) recovered 22 frames from WAL file /data/data/com.dmitrysimakov.kilogram/databases/androidx.work.workdb-wal

The application is working fine, but I want to know why this error occurs. databases/androidx.work.workdb-wal it is a Worker's journal. I use Worker to prepopulate my database.

Room.databaseBuilder(app, KilogramDb::class.java, "kilogram.db")
            .addCallback(object : RoomDatabase.Callback() {
                override fun onCreate(db: SupportSQLiteDatabase) {
                    super.onCreate(db)
                    val request = OneTimeWorkRequestBuilder<SeedDatabaseWorker>().build()
                    WorkManager.getInstance().enqueue(request)
                }
            })
            .fallbackToDestructiveMigration()
            .build()
2 Answers

This message indicates that the database hasn't been closed prior to exiting and thus that the WAL file wasn't cleaned up properly.

So when the App starts it's realising that it needs to do the cleanup of the WAL file and then does so, but issues the Error as it could indicate something serious.

To resolve the issue you need to close the database when done with it.

You may find this of interest (Richard Hipp being the main person responsible for SQLite, if you didn't already know) Continuous recovery of journal

I am using a bound service to connect to room database, so I use this code in my RoomService.onDestroy() method:

  @Override
  public final void
  onDestroy()
  {
    super
    .onDestroy();

    if(roomDatabase != null)
    {
      if(roomDatabase
         .isOpen())
      {
        roomDatabase
        .close();
      }
    }
  }

If you create your RoomDatabase instance in your Application singleton or in your Activity, you may do the same thing there (in corresponding onDestroy() method).

For convenience here is the code I use in my MainActivity class to close database in bound service:

  @Override
  protected final void
  onDestroy()
  {
    super.onDestroy();

    if(isFinishing())
    {
      if(mainViewModel != null)
      {
        mainViewModel
        .onDestroy();
      }
    }
  }

In MainViewModel.onDestroy() I send a message to bound service to close roomDatabase and then I unbind roomService:

  public final void
  onDestroy()
  {
    if(contextWeakReference != null)
    {
      final Context
      context =
      contextWeakReference
      .get();

      if(context != null)
      {
        if(roomServiceConnection != null)
        {
          if(boundToRoomService)
          { 
            sendDBCloseMessageToRoomService();

            context
            .unbindService
            (roomServiceConnection);
          }
        }
      }
    }
  }

  private void
  sendDBCloseMessageToRoomService()
  {
    try
    {
      final Message message =
      Message.obtain
      (null, MSG_DB_CLOSE);

      if(message != null)
      {
        if(messengerToRoomService != null)
        {
          messengerToRoomService
          .send(message);
        }
      }
    }
    catch(final RemoteException e)
    {
      e.printStackTrace();
    }
  }

In RoomService I catch the message to close roomDatabase:

  public class RoomService
  extends Service
  {
    @NonNull @NonNls public static final
    String DATABASE_NAME = "room_database";

    public static final int MSG_DB_CLOSE = 108;

    @Nullable public RoomDatabase roomDatabase;

    private final IBinder roomBinder = new Binder();
    private WeakReference<Context> contextWeakReference;

    @Nullable public Messenger messengerFromRoomService;
    @Nullable public Messenger messengerToRoomService;

    private static class RoomServiceHandler
    extends Handler
    {
      @Nullable private final 
      WeakReference<RoomService> roomServiceWeakReference;

      RoomServiceHandler
      (@Nullable final
       RoomService service)
      {
        if(service != null)
        {
          roomServiceWeakReference =
          new WeakReference<RoomService>
          (service);
        }
        else
        {
          roomServiceWeakReference = null;
        }
      }

      @Override
      public final void
      handleMessage
      (@Nullable final
      Message message)
      {
        if(message != null)
        {
          final int what =
          message.what;

          switch(what)
          {
            case MSG_DB_CLOSE:
            {
              handleDBCloseMessage
              (message);
              break;
            }
          }
        }
      }

      private void
      handleDBCloseMessage
      (@Nullable final
       Message message)
      {
        if(message != null)
        {
          final RoomService
          service =
          roomServiceWeakReference
          .get();

          if(service != null)
          {
            if(service
               .roomDatabase != null)
            {
              if(service
                 .roomDatabase
                 .isOpen())
              {
                service
                .roomDatabase
                .close();    
              }
            }
          }
        }
      }
    }

    @Override
    public final void
    onCreate()
    {
      super.onCreate();

      // initialize application context weak reference
      final Context
      applicationContext =
      getApplicationContext();

      if(applicationContext != null)
      {
        contextWeakReference =
        new WeakReference<Context>
        (applicationContext);

        // initialize database
        roomDatabase =
        Room
        .databaseBuilder
        (applicationContext,
        MyRoomDatabase.class,
        DATABASE_NAME)
        .build();

        if(roomDatabase != null)
        {
          // initialise your DAO here
          yourDao =
          roomDatabase
          .yourDao();
        }
      }

      final RoomServiceHandler
      roomServiceHandler =
      new RoomServiceHandler(this);

      if(roomServiceHandler != null)
      {
        messengerToRoomService =
        new Messenger(roomServiceHandler);
      }
    }

    @Nullable
    @Override
    public final IBinder
    onBind
    (@Nullable final
    Intent intent)
    {
      IBinder result = null;

      if(messengerToRoomService != null)
      {
        final IBinder
        roomBinder =
        messengerToRoomService
        .getBinder();

        if(roomBinder != null)
        {
          result = roomBinder;
        }
      }
      return result;
    }
  }
Related