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

navigation firebase coroutine

0 추천

버튼 클릭시 firebase에 존재 유무 체크하고, 있다면 정보를 가져와 다음 프래그먼트로 이동하는 

간단한 코드입니다만... 버튼을 두번이상 연속으로 누르면 오류가 뜨면서 앱이 죽습니다..

이런 경우 어찌 해결해야할지 궁금합니다...ㅎ

nextButton.setOnClickListener {  check(edit.text.toString())  }
private fun check(name: String) = MainScope().launch {
        if (viewModel.check(name)) {  //editText와 firebase document가 일치 한다면
            viewModel.getAllData(name) // document 의 모든 정보 가져오기
            navController.navigate(R.id.nextFragment) // 정보 가져온후 navigation 이동
        } else
            Toast.makeText(context, "틀렸습니다.", Toast.LENGTH_SHORT).show()
}

viewModel에서 firebase 통신은 withContext(Dispatcher.IO) 로 지정해두었고

해당 오류입니다

java.lang.IllegalArgumentException: Navigation action/destination ~~~:id/~~~ cannot be found from the current destination Destination

dkssudgktpdy (520 포인트) 님이 2022년 7월 11일 질문

2개의 답변

0 추천
 
채택된 답변
일단 버튼이 두번 눌리는 부분은 클릭시 isEnabled = false로 하셔서 방지 하시구요.
그리고 ViewModel과 coroutine scope의 사용 부분을 리뷰해보시는 게 좋을 것 같아요.
첫번째로 MainScope은 불필요해 보이구요. Activity나Fragment에서 사용해야 한다면 LifecycleScope을 사용하시면 됩니다.
ViewModel을 사용하고 있으므로withContext를 바로 호출하지 말고 viewModelScope이 라이프사이클에 안전하게 처리되어 있기 때문에 viewModelScope을 사용하는게 낫습니다.
그리고 ViewModel을 사용할 경우는 처리결과는 LiveData를 통해서 받는 것이 일반적이고
viewModel.check과 viewModel.getAllData를 호출하는 부분은 ViewModel로 옮겨서 두 함수를 모두처리하는 한개의 함수를 공개해서 View에선 all data를 가져오기 위한 구체적인 동작에 대해 모르도록 감추는 것이 좋습니다.
그리고 ViewModel에 coroutine이 두번 호출된다면 둘 중 하나의 job을 취소 해주는 것이 안전하겠죠.
spark (223,680 포인트) 님이 2022년 7월 12일 답변
dkssudgktpdy님이 2022년 7월 13일 채택됨
0 추천

위에서 설명드린 내용을 코드로 변환해 보면, 아래처럼 될 것 같습니다.

private fun check(name: String) {
       nextButton.isEnabled = false
       showProgressBar()

       viewModel.fetchAllDataByName(name).observe { isSuccess ->
              dismissProgressBar() 
              if (isSuccess) {
                 navigateToNextFragment()
                 return
              }

              handleFetchDataError()
      }
}

private fun handleFetchDataError() {
     Toast.makeText(context, "틀렸습니다.", Toast.LENGTH_SHORT).show()
      nextButton.isEnabled = true
}

 

fun fetchAllDataByName(name: String): LiveData<Boolean> = liveData {
      if (checkName(name)) {
            getAllDataByName()
            emit(true)
            return@liveData
      }

     emit(false)
} 

private suspend fun checkName(name: String) = withContext(Dispatcheres.IO) {
     ...
}

private suspend fun getAllDataByName(name: String)= withContext(Dispatcheres.IO) {
   ...
}

 

 참고로 checkName, getAllData 둥의 함수는 state holder인 ViewModel의 역할과는 상관이 없으므로 UseCase/Interactor/Repository 등으로 옮겨서 처리하는 것이 좋습니다.

그리고 check, fetchAllData 등은 어떤 일을 할 수 있는지 알 수 있도록 명확한 이름을 사용하시면 더 좋을 것 같습니다. 예를 들면, checkUserName, fetchUsers 등 처럼요.

 

spark (223,680 포인트) 님이 2022년 7월 12일 답변
liveData는 jetpack 라이브러에서 제공되는 extension function 중의 하나입니다.
정말 자세히 알려주셔서 감사합니다! 다른 내용의 질문이지만
'UseCase/Interactor/Repository 등으로 옮겨서 처리하는 것이 좋습니다.'
이부분 어떤 공부를 해야 알수있는건가요??
...