4

I have 2 Activity viz

  1. List Activity
  2. Detail Activity

The list Activity shows list of items and detail Activity is shown on clicking an item from the list. In the ListActivity we observe for fetching the feeds from the DB and once we do, we update the UI.

List page

feedViewModel.getFeeds().observe(this, Observer { feeds ->
      feeds?.apply {
            feedAdapter.swap(feeds)
            feedAdapter.notifyDataSetChanged()
      }
})

Now we have a DetailActivity page which updates the feed (item) and Activity is finished but the changes are not reflected in the ListActivity.

Details page

override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        feedViewModel.setFeedId(id)
        feedViewModel.updateFeed()
}

Feed View Model

class FeedViewModel(application: Application) : AndroidViewModel(application) {


    private val feedRepository = FeedRepository(FeedService.create(getToken(getApplication())),
            DatabaseCreator(application).database.feedDao())

    /**
     * Holds the id of the feed
     */
    private val feedId: MutableLiveData<Long> = MutableLiveData()

    /**
     * Complete list of feeds
     */
    private var feeds: LiveData<Resource<List<Feed>>> = MutableLiveData()

    /**
     * Particular feed based upon the live feed id
     */
    private var feed: LiveData<Resource<Feed>>

    init {
        feeds = feedRepository.feeds
        feed = Transformations.switchMap(feedId) { id ->
            feedRepository.getFeed(id)
        }
    }

    /**
     * Get list of feeds
     */
    fun getFeeds() = feeds

    fun setFeedId(id: Long) {
        feedId.value = id
    }

    /**
     * Update the feed
     */
    fun updateFeed() {
        feedRepository.updateFeed()
    }

    /**
     * Get feed based upon the feed id
     */
    fun getFeed(): LiveData<Resource<Feed>> {
        return feed
    }

}

For simplicity purpose some code has been abstracted. If required I can add them to trace the problem

Akshay Chordiya
  • 4,761
  • 3
  • 40
  • 52

3 Answers3

5

After lots of investigation and some idea from this answer from another question. I figured out the issue.

Issue

The DatabaseCreator(application).database.feedDao() was not creating singleton instance of the database due to which the first Activity had another instance where LiveData was listening for changes and 2nd Activity had another instance where after updating the data the callback was ignored.

Solution

Use Dagger or any other dependency injection to insure only single instance of DB and DAO is created.

Akshay Chordiya
  • 4,761
  • 3
  • 40
  • 52
  • I have the exact same problem, however I have annotated the instance of the DB and the DAOs as singletons. What did you change? By the way, I'm using Dagger 2. If you want more info here is the question I posted: https://stackoverflow.com/questions/48267724/view-not-updating-after-first-time-triggering-livedata-when-using-background-job – César Alberca Jan 16 '18 at 14:32
1

I was facing this same issue across multiple activities for same entities. To me was useful to code Room Database instance as a Singleton, like : this (step 5)

Example:

public abstract class MyDatabase extends RoomDatabase {
    private static MyDatabase mMyDatabase;
    public static MyDatabase getInstance(Context context) {
        if(mMyDatabase==null){
            mMyDatabase = Room.databaseBuilder(context.getApplicationContext(), MyDatabase.class, "app_database").build();
            return mMyDatabase;
        }
    }
}

And in every ViewModel you have (class extends from AndroidViewModel for Context parameter):

    public MyViewModel(@NonNull Application application) {
        super(application);
        mMyDatabase = MyDatabase.getInstance(application.getApplicationContext());
}

Now, in my case, every time I edit a value in configuration activities it get reflected in other activities.

Hope this help.

0

I had the same problem. In the following code, I forgot one line and the singleton did not work (the problem are the same as in the main answer).

@Database(entities = [SleepNight::class], version = 1, exportSchema = false)
abstract class SleepDatabase : RoomDatabase() {

   abstract val sleepDatabaseDao: SleepDatabaseDao

   companion object {

       @Volatile
       private var INSTANCE: SleepDatabase? = null

       fun getInstance(context: Context): SleepDatabase {
           synchronized(this) {
               var instance = INSTANCE

               if (instance == null) {
                   instance = Room.databaseBuilder(
                           context.applicationContext,
                           SleepDatabase::class.java,
                           "sleep_history_database"
                   )
                           .fallbackToDestructiveMigration()
                           .build()
                   INSTANCE = instance
               }
               return instance
           }
       }
   }
}

Forgotten line:

INSTANCE = instance