Когда я go возвращаюсь к фрагменту, наблюдателя немедленно вызывают - PullRequest
1 голос
/ 21 января 2020

У меня есть наблюдатель, чем когда он вызывается изменяет фрагменты.

Проблема в том, что когда я go назад, наблюдатель вызывается немедленно, и мое приложение вылетает с

java .lang.IllegalArgumentException: назначение навигации com.superapps.ricardo.tablepro: id / action_searchFragment_to_yourGameList2 неизвестно этому NavController.

Я не понимаю, почему он вызывается.

это единственный метод, который изменяет список

override fun onSuccess(gamePair: Pair<Int, List<BggGame>>) {
        CoroutineScope(Main).launch{
            //goToList(gamePair.second, binding.input.text.toString())
            viewModel.setGameList(gamePair.second)
        }
    }

, и это код создания модели и изменения кода фрагмента

override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
        viewModel = ViewModelProviders.of(this).get(SearchViewModel::class.java)
        viewModel.gameList.observe(viewLifecycleOwner, Observer {
            goToList(it, binding.input.text.toString())
        })
    }


    private fun goToList(games: List<BggGame>, user: String) {
        val action = SearchFragmentDirections.actionSearchFragmentToYourGameList2(user)
        val gameList = GameList()
        gameList.gameList = games
        action.gameList = gameList

        try {
            Navigation.findNavController(view!!).navigate(action)
            viewModel.gameList.removeObservers(viewLifecycleOwner)
        } catch (e: Exception){
            Log.e("a0,","a..", e)
        }
        progressDialog.dismiss()

    }

Ответы [ 2 ]

2 голосов
/ 21 января 2020

В вашем viewModel используйте SingleLiveEvent вместо MutableLiveData или LiveData.

Это класс SingleLiveEvent, вы можете использовать его в пакете util:

import android.util.Log;
import androidx.annotation.MainThread;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.MutableLiveData;
import androidx.lifecycle.Observer;
import java.util.concurrent.atomic.AtomicBoolean;

public class SingleLiveEvent<T> extends MutableLiveData<T> {

    private static final String TAG = "SingleLiveEvent";

    private final AtomicBoolean mPending = new AtomicBoolean(false);

    @Override
    public void observe(@NonNull LifecycleOwner owner, @NonNull Observer<? super T> observer) {

        if (hasActiveObservers()) {
            Log.w(TAG, "Multiple observers registered but only one will be notified of changes.");
        }

        // Observe the internal MutableLiveData
        super.observe(owner, new Observer<T>() {
            @Override
            public void onChanged(@Nullable T t) {
                if (mPending.compareAndSet(true, false)) {
                    observer.onChanged(t);
                }
            }
        });
    }


    @MainThread
    public void setValue(@Nullable T t) {
        mPending.set(true);
        super.setValue(t);
    }
}
2 голосов
/ 21 января 2020

LiveData сохраняет последнее установленное значение. При вызове observe() для LivaData, если LiveData имеет значение, наблюдатель немедленно вызывается с ранее установленным значением.

Если вы хотите использовать LiveData для «событий», таких как ваш сценарий использования, ваши действующие данные должны предоставлять объект Event, который может быть использован только один раз.

Вот пример хорошей реализации такого класса Event.

Из статьи:

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

    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * 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
}

Использование в ViewModel:

val gameList = MutableLiveData<Event<List<BggGame>>()
fun setGameList(gameList: List<BggGame>) {
    gameList.value = Event(gameList)
}

Использование в просмотре:

viewModel.gameList.observe(this, Observer {
    it.getContentIfNotHandled()?.let { // Only proceed if the event has never been handled
        goToList(it, binding.input.text.toString())
    }
})
...