简体   繁体   中英

Application Crash/Navigation issue with observer when clicking “back” from fragment

I have a fragment A which sends a search query to the network, and if the result is positive uses Android navigation component to navigate to fragment B, and its done using observers.

After navigation to fragment B, i click on "<-" arrow on the top of the screen, but instead of navigating back to fragment A it reloads fragment B again. And if using the native "back" button on the device, the app crashes with "illegalArgumentException navigation destination unknown" error.

I check the internet for clues on this issue, but all i learned is that this happens because i am using .observe in onViewCreated() and when i go back, it gets called again, and because livedata has something in it already, it just navigates me back to B.

I have tried observing in onActivityCreated(), and using getViewLifeCycleOwner, but no success... the only thing that helped is checking if livedata has observers and returning if true, before using .observe, but it seems incorrect.

This is the viewModel:

private val getAssetResult = MutableLiveData<GeneralResponse<Asset>>()
private val updateAssetResult = MutableLiveData<GeneralResponse<Int>>()
private val deleteAssetResult = MutableLiveData<GeneralResponse<Int>>()

init {
    state.value = ViewState(false)
    Log.d(TAG, "State in init: $state")
}

fun getAssetResult(): LiveData<GeneralResponse<Asset>>{

    return getAssetResult
}

fun findAsset(req: GetAssetRequest) {

    scope.launch {
        setProgressIndicator(true)
        val result = repository.getAsset(req)

        getAssetResult.postValue(result)

        setProgressIndicator(false)
    }
}

This is the fragment:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewModel = ViewModelProvider(this).get(EditAssetViewModel::class.java)
    setupViewModel()
    initFields()
}


private fun setupViewModel() {

    if (viewModel.getAssetResult().hasObservers()) // <- This is the part that prevents the app from crashing.
        return

    viewModel.getAssetResult().observe(this, Observer {
        if (it == null) return@Observer

        handleSearchResult(it)
    })

    if (viewModel.getState().hasObservers())
        return

    viewModel.getState().observe(this, Observer { handleState(it) })
}

private fun handleSearchResult(response: GeneralResponse<Asset>) {

    if (response.singleValue == null) {
        Toast.makeText(context!!, response.errorMessage, Toast.LENGTH_SHORT).show()
        return
    }

    targetFragment?.let { it ->

        val bundle = bundleOf("asset" to response.singleValue)

        when(it) {
            "UpdateLocation" ->
                Navigation.findNavController(view!!).navigate(R.id.updateLocation, bundle)
            "EditAsset" -> {
                Navigation.findNavController(view!!).navigate(R.id.editAsset, bundle)
            }
        }
    }
}

if i remove this part from the setupViewModel function:

if (viewModel.getAssetResult().hasObservers()) return

the app will either crash when clicked "back" using the device button or go back to fragment A, just to be navigated back to fragment B because of the .observe function.

重写onBackPressed()方法以处理“ <-”箭头

Seems like the LiveData that you use to signal to fragment A that it should navigate to fragment B is actually an event. An event happens only once and once it is consumed (navigation event is done), it is gone. Therefore, after navigating you need to send a message to the viewmodel that the navigation took place and that the corresponding data holder should be (eg) null again. In Fragment A you check that the new value is unequal to null, and only if this is the case, you issue the navigation event. This would prevent fragment A to immediatelly switch to B again in the back scenario.

If you want to learn more about ways to use live data for events, please refer to this article .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM