240109 D35 onBackPressedDispatcher

jericho·2024년 1월 9일

TIL

목록 보기
34/62

숙련주차 개인과제를 뽀개고 있다. 의외로 기능들이 간단간단하게 해결이 되어서, 쭉쭉 진도를 뽑을 수 있었다. 다만 뒤로가기 버튼을 눌렀을 때 종료 확인 다이얼로그를 띄우기 위해서 onBackPressed()를 사용했는데, deprecated 된 것이 신경쓰였다. 알아보니 onBackPressedDispatcher를 사용해야 했다.

그런데 이게 사용방법이 꽤 달랐다. 함수를 오버라이드하던 것에서, 콜백을 등록하는 방식으로 바뀌었다. 백버튼 입력에 대해 예측하여 먼저 동작하기 위해서 변경됐다는 듯 하다.
코드상으론 별로 바꾸지도 않았건만, 이 놈의 동작을 이해하느라 한참을 태웠다.

// onCreate
onBackPressedDispatcher.addCallback { exitDialog(this) }

// MainActivity
private fun exitDialog(callback: OnBackPressedCallback) {
    AlertDialog.Builder(this).apply {
        setTitle("종료")
        setMessage("정말 종료하시겠습니까?")
        setIcon(R.drawable.chat)

        setNegativeButton("취소", null)
        setPositiveButton("확인") { _, _ ->
            callback.isEnabled = false
            onBackPressedDispatcher.onBackPressed()
        }
    }.show()
}

위와 같이 사용한다.
콜백을 등록해준다. 콜백에서는 종료 다이얼로그를 띄우는데, 후술할 무한루프를 피하기 위해 콜백을 this로 넘겨준다.
종료 다이얼로그는 확인을 눌렀을 때만 리스너를 넣어주는데, 종료하기 위해 백버튼을 호출하여 시스템이 처리하도록 넘겨주면, 이미 등록되어있는 콜백때문에 다시 종료 다이얼로그로 들어오게 되어 무한루프에 빠진다.
이를 막기 위해서는 등록된 콜백을 없애거나, 비활성화시켜줘야 한다. 이를 위해서 콜백을 넘겨받았고, 이를 비활성화하고 백버튼을 호출해주면 앱이 정상적으로 종료된다.

deprecated된 onBackPressed를 이용하면 아래와 같다.

@Deprecated("Deprecated in Java")
override fun onBackPressed() {
    val builder = AlertDialog.Builder(this)
    builder.setTitle("종료")
    builder.setMessage("정말 종료하시겠습니까?")
    builder.setIcon(R.drawable.chat)

    val listener = DialogInterface.OnClickListener { _, _ ->
        super.onBackPressed()
    }

    builder.setNegativeButton("취소", null)
    builder.setPositiveButton("확인", listener)

    builder.show()
}

콜백을 등록하고 회피할 필요가 없고, onBackPressed를 오버라이딩하면서 거기에서 부모의 onBackPressed를 호출하기만 하면 끝이다.
이상한 dispatcher따위 버리고 onBackPressed나 쓰고 싶지만, deprecated된 메서드를 사용하다가 나중에 후회할 일이 생기면 안되니... 어쩔 수 없다.

참고로 위아래 코딩이 약간 다른데, builder에 apply를 적용하여 builder를 일일이 입력하는 것을 없앴고, 리스너를 확인에서 바로 만들어 넣었다. show()가 명확하게 분리되어 까먹지 않게 되는 것은 덤.

빌더를 따로 저장해두면 취소를 연타할 때 계속해서 재생성하는 것을 막을 수 있다...만 귀찮으니 냅두자. 아무리 연타해도 렉따위 없을만큼 가볍다.

0개의 댓글