안드로이드 suspendCoroutine를 통해 Firebase 이용하기
'크리스마스에 할 것도 없는데 앱이나 하나 만들자'라고 크리스마스 한 주 전부터 생각하고 있었다. 그 와중에 친구가 문제적 남자에서 육목을 하는 영상을 보여줬는데, 한 번 만들어보면 재밌을 것 같다고 생각했다. 그리고 생각이 드는 순간 계획을 세우고 바로 개발에 돌입했다.
처음에는 만만하게 생각하고 3일이면 앱을 다 만들 수 있을 것이라고 생각했다. 또한 계획을 길게 잡으면 쳐지기 때문에 빠른 시일 내에 앱을 임팩트 있게 개발하고 싶어서 최대한 집중해서 개발했다. 그러나 결국 계절학기가 시작됐고 앱을 다 만들지 못했다. '계절학기를 수강하면서 앱을 개발하면 되지 않나?'라고 할 수 있지만, 사실 계절학기를 두 과목 들었기 때문에 많은 시간을 투자할 수는 없었다. 또한 앱 개발이 잘 되어 오다가, 잘 풀리지 않는 오류에 직면했다. 그래서 위 사진에서 보는 바와 같이 오류를 고치느라 github에서 commit을 하지 못한 것을 확인할 수 있다.
그 오류는 계절학기가 모두 끝나고 나서, 다시 오류의 원인과 해결방법을 찾아보면서 결국 해결할 수 있었다. 그 오류는 Coroutine과 관련된 것이었고, Coroutine을 통해 해결되었다.
suspendCoroutine
*문제 원인
fun temp(database: DatabaseReference, roomNum: String): Boolean{
var rValue = true
database.addListenerForSingleValueEvent(object : ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
for (i in snapshot.children) {
if (roomNum == i.child("roomId").value.toString()) {
rValue = false
}
}
}
override fun onCancelled(error: DatabaseError) {}
})
return rValue
}
Firebase database에서 값이 있는지 유무를 확인한 후 값이 있다면 false를 반환하고, 값이 없다면 기존에 선언된 true를 반환하는 코드다. 위 코드가 필요했던 이유는 사용자가 방 번호를 입력하면 그 순간 데이터베이스에서 사용자가 입력했던 방 번호가 있는지 조회를 한 후, 사용자가 입력했던 방 번호가 있으면 방을 만들지 못하도록 하고, 방 번호가 없다면 방을 만들 수 있게 해주기 위해서 필요했다. 그러므로 미리 불러올 수 있는 값도 아니었기에 비동기식으로 처리하지 못했다.
정상적으로 구동은 되지만 위 temp function은 항상 true를 반환한다. 그 이유는 firebase의 addListenerForSingleValueEvent가 비동기식으로 돌기 때문에 결국 rValue를 true로 return한 후 firebase database 값을 조회한다. 즉 이 비동기 방식을 동기로 바꿔줘야 내가 원하는 코드가 될 수 있는 것이다.
*해결방법 : suspendCoroutine
- Kotlin 공식문서에서는 위와 같이 설명하고 있다. suspendCoroutine은 resume을 이용해서 반환할 수 있고, synchronously(동기식)으로 사용할 수 있다고 한다. 위에서 문제가 되었던 코드를 아래와 같이 수정했다.
private suspend fun isRoomAvailable(roomNum: String) =
suspendCoroutine<Boolean> {
Handler(Looper.getMainLooper()).postDelayed({
val database = FirebaseDatabase.getInstance().reference.child("roomId")
var rValue = true
database.orderByChild("roomId").equalTo(roomNum)
.addListenerForSingleValueEvent(object :
ValueEventListener {
override fun onDataChange(snapshot: DataSnapshot) {
rValue = false
it.resume(rValue)
}
override fun onCancelled(error: DatabaseError) {}
})
}, 500)
}
- equalTo를 이용해서 사용자가 입력한 roomNum 값과 같은 값이 Firebase Database에 있는지 조회한다. 그래서 같은 값이 있다면 addListenerForSingleValueEvent가 호출되는데 이 때, rValue를 false로 바꾼다. 그 후 같은 값이 있으므로 더 조회해볼 필요 없이 바로 rValue 값과 함께 resume한다.
- it.resume(반환값)을 이용해서 코드 중 원하는 위치에서 return할 수 있다. 정확히 위 오류에서 발생했던 문제점을 해결할 수 있는 코드였다. 또한 it.resumewith(Result.success(반환값)), it.resumewith(Result.failure(AssertionError()))와 같이 반환 성공, 실패로 나누어서 반환할 수도 있다.
- 또한 suspendCoroutine을 받는 부분에서는 Coroutine이나 또 다른 suspend function에서 호출해야 한다. 그냥 oncreate 안에서 호출하거나 다른 함수에서 호출할 수 없다.
'프로그래밍 > 안드로이드+코틀린' 카테고리의 다른 글
코틀린 물음표와 느낌표 (0) | 2021.01.31 |
---|---|
안드로이드 볼륨키 클릭 이벤트 관리 (0) | 2021.01.29 |
[Android] 부드러운 화면 전환과 좌표체계 (0) | 2021.01.18 |
안드로이드 스튜디오 실수로 삭제한 파일 복구 (0) | 2020.12.29 |
안드로이드 스튜디오 뒤로가기(back) 버튼 감지하는 법 (0) | 2020.12.27 |