当我返回片段时,观察者会立即被调用

时间:2020-01-21 12:11:08

标签: android kotlin observers

我有一个观察者,而不是它被称为改变片段时。

问题是,当我返回时,观察者立即被调用,我的应用程序因

崩溃

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 个答案:

答案 0 :(得分:2)

LiveData保留最后设置的值。在observe()上调用LivaData时,如果LiveData有一个值,则使用先前设置的值立即调用观察者。

如果您想将LiveData用于诸如用例之类的“事件”,则实时数据应公开一个Event对象,该对象只能使用一次。

这里是此类Event类的优秀implementation的示例。

摘自文章:

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())
    }
})

答案 1 :(得分:2)

在您的viewModel中使用SingleLiveEvent代替MutableLiveDataLiveData

这是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);
    }
}