SwitchMap не срабатывает при изменении LiveData

Я следовал этой документации: Преобразования | Android-разработчики

Идея состоит в том, что при изменении значения currentList данные activeTasks, completedTasks и importantTasks будут автоматически обновляться, а затем наблюдатели будут обновлять пользовательский интерфейс.

Проблема, с которой я сталкиваюсь, заключается в том, что при изменении значения currentList карты switchMaps, которые я назначил LiveData, вообще не запускаются.

Я пытался найти решение, но я точно не знаю, как погуглить об этой проблеме.

вот моя ViewModel:

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

    companion object {
        const val TAG: String = "AppViewModel"
    }

    private val dataSource: TaskDao

    val lists: LiveData<List<TaskList>>
    var lastAction: Int = 1
    val currentList = MutableLiveData(1)

    var currentIndex: Int = -1
        set(value) {
            field = value
            Log.d(TAG, "currentIndex setter: index = $field")
        }

    init {
        val db = AppDatabase.getInstance(application)
        dataSource = db.taskDao()
        lists = dataSource.getAllLists()
    }

    fun importantTasks(): LiveData<List<Task>> = currentList.switchMap {
//      this line for tracking changes
//      and this how i know it not getting triggered
        Log.d(TAG, "importantTasks: updated")

        dataSource.getImportant(it)
    }

    fun activeTasks(): LiveData<List<Task>> = currentList.switchMap {
        Log.d(TAG, "activeTasks: updated")
        dataSource.getActiveTasks(it)
    }

    fun completedTasks(): LiveData<List<Task>> = currentList.switchMap {
        Log.d(TAG, "completedTasks: updated")
        dataSource.getCompletedTasks(it)
    }

    fun setCurrentList(id: Int){
        currentList.value = id
    }

    fun upsert(task: Task) = viewModelScope.launch(Dispatchers.IO) {
        task.listId = currentList.value!!
        dataSource.upsert(task)
        Log.d(TAG, "upsert: task upserted at ${task.listId}")
    }

    fun delete(task: Task) = viewModelScope.launch(Dispatchers.IO) {
        task.listId = currentList.value!!
        dataSource.delete(task)
        Log.d(TAG, "delete: task deleted at ${task.listId}")
    }

    fun upsertList(list: TaskList) = viewModelScope.launch(Dispatchers.IO) {
        dataSource.upsertList(list)
        Log.d(TAG, "upsertList: updated: id = ${list.id} , name = ${list.name}")
    }
}

вот как я могу изменить значение currentList:

    override fun onItemClicked(id: Int) {
        viewModel.setCurrentList(id)
        dismiss()
    }

ОБНОВЛЯТЬ

Я попробовал это в новом проекте, чтобы проверить, была ли проблема во мне, но он отлично работает.

модель просмотра:

class AppViewModel : ViewModel() {

    val listId = MutableLiveData(0)

    fun getUsers(): LiveData<List<User>> = listId.switchMap {
        Log.d("AppViewModel", "getUsers: updated")
        data(it)
    }

    fun data(id: Int): LiveData<List<User>> {
        val users = if (id == 0) List(30) { i -> User(i, "User$i", "password$i") }
        else List(25) { i -> User(i, "User$i", "password$i") }
        return MutableLiveData(users)
    }
}

как я изменил значение listId:

class MainActivity : ComponentActivity() {
    val viewModel: AppViewModel by viewModels()

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

        val text = findViewById<TextView>(R.id.textCounter)
        val button = findViewById<Button>(R.id.buttonSwitch)

        viewModel.getUsers().observeForever{
            Log.d("MainActivity", "onCreate: observer triggered")
            text.text = "${it.size}"
        }
        button.setOnClickListener {
            viewModel.listId.value = if (viewModel.listId.value == 1) 0 else 1
        }
    }
}

Я использую RoomDatabase для первого проекта, возможно, это причина моей проблемы (не уверен в этом), потому что во втором я использую определенный набор данных, и он работает нормально.

Вы должны observe LiveData, возвращенные switchMap, чтобы он действительно что-то делал. Где вы наблюдаете каждую из этих LiveData?

ianhanniballake 23.07.2023 05:59

Я помещаю наблюдателя во фрагмент для каждого, switchMap срабатывает при инициализации currentList, но больше не срабатывает, когда я изменяю значение currentList.

Hna Naut 23.07.2023 06:38
1
2
54
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Проблема здесь в том, что вы определяете importantTasks(), activeTasks() и completedTasks() как функции, а не LiveData свойства. Функция switchMap вызывается только один раз, когда вы впервые вызываете эти функции, а затем она не вызывается снова, потому что функции больше не вызываются.

Вместо функций вы должны определить importantTasks, activeTasks и completedTasks как LiveData свойства в вашей ViewModel. Вот как это сделать:

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

    // ...

    val importantTasks: LiveData<List<Task>> = currentList.switchMap { id ->
        Log.d(TAG, "importantTasks: updated")
        dataSource.getImportant(id)
    }

    val activeTasks: LiveData<List<Task>> = currentList.switchMap { id ->
        Log.d(TAG, "activeTasks: updated")
        dataSource.getActiveTasks(id)
    }

    val completedTasks: LiveData<List<Task>> = currentList.switchMap { id ->
        Log.d(TAG, "completedTasks: updated")
        dataSource.getCompletedTasks(id)
    }

    // ...
}

Теперь каждый раз, когда currentList изменяется, importantTasks, activeTasks и completedTasks будут автоматически обновляться, потому что они являются LiveData свойствами, которые наблюдают currentList через функцию switchMap.

Хотя это правда, что они должны изменить свой код на то, что есть у вас, чтобы предотвратить возможность возникновения этой проблемы после воссоздания действия/фрагмента, это, вероятно, не единственная проблема, потому что они не упомянули проблему, возникающую только после поворота экрана. Если функция вызывается один раз, а затем наблюдаются возвращенные оперативные данные функции, они будут обновляться по мере изменения исходных оперативных данных, а не только при повторном вызове функции. Проблема возникает, когда функция вызывается снова и наблюдается новый экземпляр.

Tenfour04 23.07.2023 14:41

я переключаю эти живые данные с функций на свойства, как вы сказали, но проблема остается прежней. Я застрял с этой проблемой уже 2 дня, это мой первый раз, когда я имею дело с viewmodel + livedata, поэтому у меня мало знаний. может быть, вы хотите взглянуть на мой проект: Todo. Спасибо за помощь!

Hna Naut 23.07.2023 15:37
Ответ принят как подходящий

Я понял!

Проблема вызвана определением 2 разных экземпляров моделей представления в 2 разных местах (MainActivity и фрагмент внутри ViewPager2 в этом случае) Как я определяю модели просмотра:

val viewModel: AppViewModel by viewModels()

и я думал, что они используют один и тот же context, и когда я изменю значение в MainActivity, фрагмент заметит это, но нет. Несмотря на то, что фрагмент находится внутри действия, они использовали другой контекст (я думаю). Вот как я решил проблему:

  • в основной активности:
val viewModel: AppViewModel by viewModels()
  • во фрагменте:
lateinit var viewModel: AppViewModel

    override fun onCreateView(): View {
        viewModel = ViewModelProvider(requireActivity()).get(AppViewModel::class.java)
    }

Хорошего дня!!

Другие вопросы по теме