마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

Navigation component 이동관련 오류

0 추천

BFragment에서 CFragment로 이동을 하는데 처음 BFragment에 진입했을때는 잘 이동이 됩니다.(여러번 B에서 C로 왔다갔다 가능) 하지만 BFragment 이전에 (이전에 AFragment가 있다고 가정하고) AFragment로 갔다가 다시 BFragment로 돌아오면 CFragment로 이동이 되지 않습니다.. 라이브러리 자체 버그일까요??

 

BFragment

class BFragment : BaseFragment<FragmentBBinding>(R.layout.fragment_b) {
    private val viewModel by activityViewModels<LocationViewModel>()

    //뒤로가기 클릭
    fun backBtn(view: View) {
        view.findNavController().popBackStack()
    }

    override fun init() {
        binding.fragment = this
        observeViewModel()
    }

    private fun observeViewModel() {
        viewModel.viewEvent.observe(requireActivity(), {
            it.getContentIfNotHandled()?.let { event ->
                Log.d("TAG", "Event : $event")
                when (event) {
                    "SUCCESS" -> {
                        Log.d("TAG", "SUCCESS")
                        this@LocationFragment.findNavController().navigate(
                            LocationFragmentDirections.actionLocationFragmentToLocationSelectFragment(
                                "location"
                            )
                        )
                    }
                    "ERROR" -> shortShowToast("오류가 발생했습니다")
                }
            }
        })
    }

    fun clickAddressSearchBtn(view: View) {
        if (!TextUtils.isEmpty(binding.query.text.toString())) viewModel.searchAddress(
            KEY,
            "similar",
            1,
            10,
            binding.query.text.toString()
        ) else shortShowToast("주소를 입력해 주세요")
    }
}

 

ViewModel 코드

@HiltViewModel
class LocationViewModel @Inject constructor(
    private val addressUseCase: SearchAddressUseCase,
    private val dataStore: DataStoreModule
) : BaseViewModel() {

    fun searchAddress(
        Authorization: String,
        analyze_type: String,
        page: Int,
        size: Int,
        query: String
    ) {
        viewModelScope.launch {
            addressUseCase.execute(Authorization, analyze_type, page, size, query)
                .let { response ->
                    try {
                        _searchAddressResponse.value = response
                        viewEvent("SUCCESS")
                    } catch (e: Exception) {
                        viewEvent("ERROR")
                    }
                }
        }
    }
}

 

안솝우화 (200 포인트) 님이 2021년 11월 6일 질문
버그는 아닐 겁니다. 코드를 보니까 프레그먼트를 이동하는 코드는
LocationFragmentDirections.actionLocationFragmentToLocationSelectFragment를 이용하는 곳 뿐인데, 이 인스턴스를 B -> C, B -> A -> C사이에 공유한다면 당연히 LocationFragmentDirections.actionLocationFragmentToLocationSelectFragment에 정의된 대로만 네비게이션이 동작을 하겠죠. 아마도 LocationFragmentDirections에는 B -> C로 가는 것만 정의가 되어 있을 것 같은데요. 네이게이션 그래프가 어떻게 구성되어 있는지는 모르겠지만, LocationFragmentDirections.actionLocationFragmentToLocationSelectFragment는 딱 B -> C만 네비게이션이 가능할 거예요.
제 댓글을 다시 보니 질문과 좀 다른 답변을 했네요.
한가지 추측할 수 있는 시나리오는 뷰모델의 LiveData를 통해서 네비게이션 이벤트를 처리할 경우 라이프사이클로 인해 다른 프레그먼트로 이동했다 돌아올 경우, observer가 다시 액티브상태가 되고 LiveData가 value안에 있던 값을 다시 프레그먼트에 알려주게 됩니다. 이 경우라면 말씀하신대로 B 에서 C 로 이동이 되지 않습니다.
하지만, 올려주신 코드로는 별 이상은 발견하지 못하겠네요. 이미 Event를 사용해서 한번 subscribe한 이벤트도 처리하고 계시고. 관련 네비게이션 그래프와 C 프레그먼트에서 네비게이션 관련해서 어떤 코드가 있는지 확인이 필요할 것 같습니다.

1개의 답변

+1 추천
 
채택된 답변

다시보니 BFragment의 init()에서 초기화를 하는데, 여기서 뷰모델의 LiveData 를  observe하는 특별한 이유가 있나요? 이렇게 하면 B가 A로 이동하게 되면 LiveData를 더이상 observe 하지 않게 되는데, 이 때 다시 A -> B로 돌아오게 되면 init은 호출되지 않고 onCreate -> onViewCreated이벤트를 타서 ViewModel의 LiveData를 observe 를 해주는데 문제가 있을 것 같은 생각이 드는데요...
Fragment에서 뷰모델 observe를 하는 시점은 onViewCreated에서 하는 것이 제일 좋습니다. observeViewModel() 를 onViewCreated 로 옮겨보시겠어요?

spark (105,040 포인트) 님이 2021년 11월 8일 답변
안솝우화님이 2021년 11월 8일 채택됨
제 질문은 this@LocationFragment.findNavController().navigate(
                            LocationFragmentDirections.actionLocationFragmentToLocationSelectFragment(
                                "location"
                            )
                        )


 this@LocationFragment.findNavController().navigate(R.id.locationSelectFragment)
로 바꿨을 때 이동이 되냐는 것이었는데, 그렇다는 건가요?
이동은 됩니다만 앞에 상황과 똑같은 현상이 일어납니다
어렵네요. 님의 코드를 보면  LocationViewModel.viewEvent.observe할 때 Event가 매번 프레그먼트 쪽으로 넘어오지 않는 것 같네요. 물론 SUCESS가 넘어올 때도 있지만 다른 프레그먼트를 갔다올 때 아예  프레그먼트에서 viewEvent를 한번만 받고는  받지 못하는 것 같아요.
제가 보기에는 ViewEvent의 버그 같습니다.

//LocationViewModel

val searchAddressResult: LiveData<Boolean> get() = _searchAddressResult
private val _searchAddressResult =  SingleLiveEvent<Boolean>()

fun searchAddress(
        Authorization: String,
        analyze_type: String,
        page: Int,
        size: Int,
        query: String
    ) {
        viewModelScope.launch {
            addressUseCase.execute(Authorization, analyze_type, page, size, query)
                .let { response ->
                    try {
                        _searchAddressResponse.value = response
                        _searchAddressResult.postValue(true)
                        //viewEvent("SUCCESS")
                    } catch (e: Exception) {
                        _searchAddressResult.postValue(false)
                        //viewEvent("ERROR")
                    }
                }
        }
    }


//LocationFragment

private fun observeViewModel() {
        viewModel.searchAddressResult.observe(viewLifecycleOwner) { isSuccess ->
            if (isSuccess) {
                this@LocationFragment.findNavController().navigate(
                    LocationFragmentDirections.actionLocationFragmentToLocationSelectFragment(
                        "location"
                    )
                )
                return@observe
            }

            shortShowToast("검색한 주소의 값이 없습니다")
        }
}
흠.. 그렇군요..! 길게 답변해주셔서 감사합니다!
...