0

I have problem when I come back in the activity from another. In my activity I use a ViewModel and, when the activity is created I call challengeDayViewModel.daysForChallenge and I observe the changes. When data are ready I set them on the adapter. In the adapter when I click on item I call challengeDayViewModel.getChallengeDay(dayId) and observe for the result. When it is ready I start activity for result passing a property of the object retrieved. In the CountdownActivity I do some things and when I come back, in the onActivityResult method I update my data in DB using challengeDayViewModel.setDayDone(selectedDayId) and the problem is here. Commenting the row where I update the data in DB everything work perfectly but if I add the row for the update, the update is done but after he re-run the rown startActivityForResult(intent, ActivityRequestCode.COUNTDOWN_CODE) opening again the CountdownActivity (I see in debug)

class ChallengeDayActivity: AppCompatActivity() {

    private var selectedDayId: Long = 0
    private lateinit var adapter: ChallengeDayAdapter

    private val challengeDayViewModel: ChallengeDayViewModel by lazy {
        ViewModelProvider.AndroidViewModelFactory.getInstance(application).create(ChallengeDayViewModel::class.java)
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_challenge_day)

        val recyclerView = challenge_day_recyclerview
        adapter = ChallengeDayAdapter(this)

        recyclerView.adapter = adapter
        recyclerView.layoutManager = GridLayoutManager(this, 3)

        challengeDayViewModel.daysForChallenge.observe(this, Observer { challengeDayList ->
            challengeDayList.let {
                adapter.setChallengeDay(challengeDayList)
                adapter.notifyDataSetChanged()
            }
        })

        adapter.goToCountdown = { dayId ->
            selectedDayId = dayId
            challengeDayViewModel.getChallengeDay(dayId).observe(this, Observer { challengeDay ->
                val intent = Intent(this, CountdownActivity::class.java).apply {
                    putExtra("seconds", challengeDay.seconds)
                }

                startActivityForResult(intent, ActivityRequestCode.COUNTDOWN_CODE)
            })
        }
    }

    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)
        if (requestCode == ActivityRequestCode.COUNTDOWN_CODE && resultCode == Activity.RESULT_OK) {
            challengeDayViewModel.setDayDone(selectedDayId)
            Snackbar.make(coordinator, "BUBU", Snackbar.LENGTH_LONG).show()
        }

    }
}

My viewmodel is

class ChallengeDayViewModel: ViewModel() {

    private val challengeDayRepository: ChallengeDayRepository

    val daysForChallenge: LiveData<List<ChallengeDay>>

    init {
        val challengeDayDao = database.challengeDayDao()

        challengeDayRepository = ChallengeDayRepository(challengeDayDao)

        daysForChallenge = challengeDayRepository.getChallengeDayForStandardChallenge()
    }

    fun setDayDone(challengeDayId: Long) = viewModelScope.launch(Dispatchers.IO) {
        challengeDayRepository.setDayDone(challengeDayId)
    }

    fun getChallengeDay(challengeDayId: Long): LiveData<ChallengeDay> = challengeDayRepository.getChallengeDay(challengeDayId)
}

My DAO

@Dao
interface ChallengeDayDao {

    @Query("SELECT * FROM CHALLENGE_DAY WHERE CHD_ID_PK = :challengeDayId")
    fun getChallengeDay(challengeDayId: Long): LiveData<ChallengeDay>

    @Query("SELECT * FROM CHALLENGE_DAY WHERE CHG_ID_FK = :challengeId")
    fun getChallengeDayForChallenge(challengeId: Long): LiveData<List<ChallengeDay>>

    @Query("SELECT CHD.* FROM CHALLENGE_DAY CHD JOIN CHALLENGE CHG ON CHD.CHG_ID_FK = CHG.CHG_ID_PK WHERE CHG.CHG_STANDARD = 1")
    fun getStandardChallengeDayForChallenge(): LiveData<List<ChallengeDay>>

    @Query("UPDATE CHALLENGE_DAY SET CHD_DONE = 1 WHERE CHD_ID_PK = :challengeDayId")
    suspend fun setDayDone(challengeDayId: Long)

    @Insert
    suspend fun insert(challengeDay: ChallengeDay)

    @Insert
    suspend fun insertAll(challengeDay: List<ChallengeDay>): List<Long>
}

In CountdownActivity for coming back I have this code

alert("Test message", "Test title") {
    okButton {
        setResult(Activity.RESULT_OK)
        finish()
    }
}.show()

Here the code for Repository

class ChallengeDayRepository(private val challengeDayDao: ChallengeDayDao) {

    @WorkerThread
    fun getChallengeDay(challengeDayId: Long): LiveData<ChallengeDay> = challengeDayDao.getChallengeDay(challengeDayId)

    @WorkerThread
    fun getAllChallengeDaysForChallenge(challengeId: Long): LiveData<List<ChallengeDay>> = challengeDayDao.getChallengeDayForChallenge(challengeId)

    @WorkerThread
    fun getChallengeDayForStandardChallenge(): LiveData<List<ChallengeDay>> = challengeDayDao.getStandardChallengeDayForChallenge()

    @WorkerThread
    suspend fun setDayDone(challengeDayId: Long) = challengeDayDao.setDayDone(challengeDayId)

    @WorkerThread
    suspend fun insert(challengeDay: ChallengeDay) = challengeDayDao.insert(challengeDay)

    @WorkerThread
    suspend fun insertAll(challengeDay: List<ChallengeDay>): List<Long> = challengeDayDao.insertAll(challengeDay)

}
Dennis A. Boanini
  • 477
  • 1
  • 5
  • 19

2 Answers2

2

Same question has already been asked in: this thread

Check answers in that post. One of solutions to prevent observe duplication, would be also to use SingleEvent LiveData: - source article can be found here, meaning that you consume data only once.

open class Event<out T>(private val content: T) {

    var hasBeenHandled = false
        private set

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content
}
mmmatey
  • 666
  • 8
  • 15
0

Inspired by this thread I write this that resolve my issue.

fun <T> LiveData<T>.observeOnce(lifecycleOwner: LifecycleOwner, observer: Observer<T>) {
    observe(lifecycleOwner, object: Observer<T> {
        override fun onChanged(t: T?) {
            observer.onChanged(t)
            removeObserver(this)
        }
    })
}
Dennis A. Boanini
  • 477
  • 1
  • 5
  • 19