오랜만에 구글 플레이 콘솔에 들어가서 이것저것 구경하고 있었는데 이런게 눈에 띄었다.
공지가 된지는 상당한 시간이 지났지만, 뒤늦게 확인을 하게 되었다.
최근 플레이 스토어에 출시를 목적으로 진행하고 있는 프로젝트의 targetSdkVersion이 29로 설정되어 있었기 때문에, 이를 30으로 수정하여 마저 구현해야 했다.
그런데 API 30 수준부터는 Handler의 생성자 2가지가 deprecated 되었고, 이를 수정할 때 필요한 내용을 정리하고자 이번 글을 작성하게 되었다.
아래의 모든 내용은 stackoverflow의 게시글을 참고했으며, 해당 게시물의 링크는 아래와 같다.
https://stackoverflow.com/questions/61023968/what-do-i-use-now-that-handler-is-deprecated
Handler가 생성되는 동안, Looper가 암묵적으로 선택되면 여러가지 버그가 발생할 수 있다고 한다.
이러한 가능성을 차단하기 위해서는 명시적으로 Looper를 선언해야 하므로, Handler()와 Handler(Handler.Callback)이 deprecated 된 것 같다.
정확하고 자세한 내용은 공식 문서를 참고하면 될 것 같다.
https://developer.android.com/reference/android/os/Handler#Handler()
Handler를 사용하는 이유로는 여러가지가 있겠지만, 이 게시글에서는 background thread에서 작업을 수행하고, 그 결과를 UI에 반영하기 위해서 Handler를 사용한다.
우선 지금까지 사용했던 Thread와 Handler의 구조부터 살펴보자.
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에 반영하기 위한 Thread와 Handler의 구조이다. (물론 이 코드보다 효율이 더 좋은 코드는 많겠지만..)
이제 이 코드에서 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를 사용하는 방법도 있지만... 처음 보는 내용이기에 나중에 더 알아봐야 할 것 같다.