Android/note

[Android] Parameter specified as non-null is null

김 안개 2023. 9. 28. 22:11

문제 상황

java.lang.NullPointerException: Parameter specified as non-null is null: method kotlin.jvm.internal.Intrinsics.checkNotNullParameter, parameter think

 

아이템 삭제 버튼을 클릭하면 NullPointerException 발생


해결 과정

첫번째로 했던 일은 Logcat 을 살펴보면 링크로 ThinkPresenter 의 getThink() 메소드를 가리키길래 거기서 문제가 있나 싶었다.

 

 

그런데 삭제를 했는데 getThink() 가 다시 호출이 되나? 싶어서 아래의 사진과 같이 삭제를 했을때의 흐름을 살펴보았다.

 

예상했던 화면 흐름은

 

 

삭제를 마치고 popBackStack() 을 하면 바로 생각 리스트로 돌아간다고 예상했지만, 호락호락하지 않은 놈 .. 

 

 

중간에 한 과정을 생각하지 못했다.

 

 

삭제 다이얼로그가 끝나면 원래 사용자에게 보여졌던 생각 화면(4번째)이 보여진 후, 생각 목록(5번째)로 넘어가게 된다.

popBackStack() 을 해서 사용자에게는 생각 화면(4번째)가 보이지 않겠지만, 이를 처리해주어야 한다는 것을 알게되었다.

 

sealed class ThinkUiState {
    object Loading: ThinkUiState()
    data class Success(val think: Think) : ThinkUiState()
    data class Failed(val tag: String, val message: String?) : ThinkUiState()
}

 

그래서 Ui 데이터를 담당했던 UiState 클래스의 Success 부분의 think 매개변수를 nullable 하도록 변경해주었다.

 

sealed class ThinkUiState {
    object Loading: ThinkUiState()
    data class Success(val think: Think?) : ThinkUiState()
    data class Failed(val tag: String, val message: String?) : ThinkUiState()
}

 

null 인 값도 들어올 수 있도록 매개변수에 ?를 붙여주면 끝!

 

private fun showThink(thinkId: Long) {
        presenter.getThink(thinkId)

        lifecycleScope.launch {
            (presenter as ThinkPresenter).thinkUiState.collectLatest {
                when(it) {
                    is ThinkUiState.Loading -> { }
                    is ThinkUiState.Success -> {
                        binding.thinkToolbar.title = "${args.position + 1}번째 생각"
                        binding.thinkContents.apply {
                            setText(it.think.think)
                        }
                        binding.thinkDate.text = it.think.registDate.substring(0, 10)

                        showMultiDialog(it.think)
                    }
                    is ThinkUiState.Failed -> { }
                }
            }
        }
    }

 

생각 화면에서 Success 를 처리해주던 부분에서도 null 을 처리할 수 있도록 아래와 같이 수정해주었다.

 

    private fun showThink(thinkId: Long) {
        presenter.getThink(thinkId)

        lifecycleScope.launch {
            (presenter as ThinkPresenter).thinkUiState.collectLatest {
                when(it) {
                    is ThinkUiState.Loading -> { }
                    is ThinkUiState.Success -> {
                        if(it.think == null) {
                            binding.root.findNavController().popBackStack()
                        } else {
                            binding.thinkToolbar.title = "${args.position + 1}번째 생각"
                            binding.thinkContents.apply {
                                setText(it.think.think)
                            }
                            binding.thinkDate.text = it.think.registDate.substring(0, 10)

                            showMultiDialog(it.think)
                        }
                    }
                    is ThinkUiState.Failed -> { }
                }
            }
        }
    }

 

null 의 값이 들어오면 삭제가 되었다는 의미이므로 popBackStack() 으로 처리해주었다!


결과 화면