[Android] Handler() 가 Deprecated 되었다고??

김병수·2021년 7월 6일
0
post-thumbnail

오랜만에 구글 플레이 콘솔에 들어가서 이것저것 구경하고 있었는데 이런게 눈에 띄었다.

공지가 된지는 상당한 시간이 지났지만, 뒤늦게 확인을 하게 되었다.
최근 플레이 스토어에 출시를 목적으로 진행하고 있는 프로젝트의 targetSdkVersion이 29로 설정되어 있었기 때문에, 이를 30으로 수정하여 마저 구현해야 했다.
그런데 API 30 수준부터는 Handler의 생성자 2가지가 deprecated 되었고, 이를 수정할 때 필요한 내용을 정리하고자 이번 글을 작성하게 되었다.

아래의 모든 내용은 stackoverflow의 게시글을 참고했으며, 해당 게시물의 링크는 아래와 같다.
https://stackoverflow.com/questions/61023968/what-do-i-use-now-that-handler-is-deprecated

Deprecated 된 이유부터 알아보자

Handler가 생성되는 동안, Looper가 암묵적으로 선택되면 여러가지 버그가 발생할 수 있다고 한다.
이러한 가능성을 차단하기 위해서는 명시적으로 Looper를 선언해야 하므로, Handler()Handler(Handler.Callback)이 deprecated 된 것 같다.

정확하고 자세한 내용은 공식 문서를 참고하면 될 것 같다.
https://developer.android.com/reference/android/os/Handler#Handler()

해결방법

Handler를 사용하는 이유로는 여러가지가 있겠지만, 이 게시글에서는 background thread에서 작업을 수행하고, 그 결과를 UI에 반영하기 위해서 Handler를 사용한다.

우선 지금까지 사용했던 ThreadHandler의 구조부터 살펴보자.

val myHandler: MyHandler = MyHandler()

inner class MyHandler : Handler() {
    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)

        val bundle: Bundle = msg.data
        if (!bundle.isEmpty) {
            // UI 작업은 여기에
        }
    }
}

inner class MyThread : Thread() {
    var stopFlag = false
    
    override fun run() {
    	while (!stopFlag) {
            val message = myHandler.obtainMessage()
            val bundle: Bundle = Bundle()
                
            // background thread에서 수행할 작업은 여기에
            
            message.data = bundle
            myHandler.sendMessage(message)
            sleep(300)
        }
    }

    fun threadStop(flag: Boolean) {
        this.stopFlag = flag
    }
}

background에서 일정한 주기마다 작업을 반복하고, 작업의 결과를 UI에 반영하기 위한 ThreadHandler의 구조이다. (물론 이 코드보다 효율이 더 좋은 코드는 많겠지만..)

이제 이 코드에서 Handler()Handler(Looper.getMainLooper())로 수정하기만 하면 deprecated 문제는 해결된다!
아래는 수정한 코드이다.

val myHandler: MyHandler = MyHandler()

inner class MyHandler : Handler(Looper.getMainLooper()) {
    override fun handleMessage(msg: Message) {
        super.handleMessage(msg)

        val bundle: Bundle = msg.data
        if (!bundle.isEmpty) {
            // UI 작업은 여기에
        }
    }
}

inner class MyThread : Thread() {
    var stopFlag = false
    
    override fun run() {
    	while (!stopFlag) {
            val message = myHandler.obtainMessage()
            val bundle: Bundle = Bundle()
                
            // background thread에서 수행할 작업은 여기에
            
            message.data = bundle
            myHandler.sendMessage(message)
            sleep(300)
        }
    }

    fun threadStop(flag: Boolean) {
        this.stopFlag = flag
    }
}

이 외에도 맨 처음에 언급했던 stackoverflow 게시글을 읽어보면 Excutor를 사용하는 방법도 있지만... 처음 보는 내용이기에 나중에 더 알아봐야 할 것 같다.

profile
주니어 개발자

0개의 댓글