마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

android 예외처리 test중 오류 질문합니다

0 추천

아까랑 비슷한 질문인거 같은데 좀 이해가 안되네요

 

 

이런식으로 for문을 다 돌고 found가 false값일때 Log를 찍어주는 방식으로 test를 했습니다.마냐게 찾는다면 found는 true가 되며 또 Log를 찍어줬습니다.코드를 보시면 이해가실겁니다.여기서 결과값을 보면 

 

이런식으로 두개의 Log가 찍힙니다.하나만 찍혀야되는데, 첫번째 사진에서   

FBRef.userInfo.addValueEventListener(postListener)

이코드는 postListener를 호출할것이고 그러면 먼저 실행되면서 유저이름을 찾으면 true값이 되어서 그 밑의 코드인 

Log.d(TAG,"유저이름 못찾음") 

이 로그는 찍히면 안됩니다. 그런데 로그가 찍힙니다.. 왜그런걸까요 순서로 봤을때 먼저 호출하고 그다음에 검사를 하는것인데 왜 false값으로 인식이 되는걸까요 아 그리고 첫번째 사진의 함수는 firebase에 realtime database에서 userNickname값을 가져오는 함수입니다.

2달째 코린이 (380 포인트) 님이 2021년 7월 27일 질문

1개의 답변

0 추천

로그의 위치가 잘못 되었네요. ValueEventListener는 비동기로 동작을 한다는 점을 염두에 두세요. 님이 ValueEventListener 밖에 변수를 둔다고 해서 이게 순차적으로 실행되는게 아니예요.  콜백이 있을 경우에는 콜백안에서 처리를 다하시려고 하면 처리가 힘들어져요. 콜백안에 있는 걸 밖으로 빼네세요. 예를 들면,

private fun getCurrentUserNickname(userFound: (UserInfoItemModel?) -> Unit) {
    val postListener = objet: ValueEventListener {
         overide onDataChange(snapshot: DataSnapshot) {
               for (datamodel in snapshot.children) {
                     if (FBAuth.getUid() == datamodel.key) {
                           val currentUser = datamodel.getValue(UserInfoItemModel::class.java)
                           userFound(currentUser)
                           return
                     }
               }
         }
    }

    ...
}

getCurrentUserNickname { currentUser: UserInfoItemModel? ->
    if (currentUser == null) {
          doSomething()
          return
    }

    currentUserNickname = currentUser.nickname
}

 

파이이베이스에서 currentUser.uid에 해당하는 데이터만 가져오시면 일일이 비교하지 않으셔도 될 것 같은데, 파이어베이스 쿼리를 찾아보세요.

spark (227,470 포인트) 님이 2021년 7월 27일 답변
진짜 따라하니까 되네요 그런데 아에 처음본 방식이라 어떻게 작동하는건지 잘모르겠네요.일단 매개변수 userFound: (UserInfoItemModel?) -> Unit 이거랑
 userFound(currentUser)
                           return 랑
함수를 호출할때도 원래는 getCurrentUserNickname() 이런식으로 호출하는데
getCurrentUserNickname { currentUser: UserInfoItemModel? ->이런식으로 호출하는것은 currentUser을 받기위함인가요??
음 그리고 android는 기본 비동기식 프로그램이라고 알고있으면되나요?아니면 기본적으로는 동기식인데 ValueEventListener처럼 비동기식 함수가 있는건가요
(UserInfoItemModel?) -> Unit를 Lamdba expression라고 합니다. 쉽게 말하면 함수의 정의를 파라미터를 받아서 사용하는 겁니다. ()안은 파라미터 타입은 -> 뒤의 Unit은 리턴 타입을 말합니다. 어떤 함수든 람다식의 정의에 부합하면 변수처럼 사용할 수 있습니다.
예를 들면
fun setUserInfo(userInfo: UserInfoItemModel?) {...}
fun updateUserInfo(userInfo: UserInfoItemModel?) {...}
fun deleteUserInfo(userInfo: UserInfoItemModel?) {...}
모두 파라미터로  UserInfoItemModel? 를 받고 있고 Unit을 리턴하기 때문에 해당 람다식으로 대체할 수 있습니다.
val setUserInfo =  (UserInfoItemModel?) -> Unit = { userInfo ->
}

val updateUserInfo =  (UserInfoItemModel?) -> Unit = { userInfo ->
}

val deleteUserInfo =  (UserInfoItemModel?) -> Unit = { userInfo ->
}

함수를 이름까지 직접 정의하지 않고 함수의 정의된 형태대로 사용할 경우가 있다면 유용하면 쓰일 수 있습니다. 코틀린은 결국 자바로 변환되므로 내부적으로는 자바의 Function insance가 생성됩니다. 따라서 로컬 스콥이 아닐 경우 사용 후 메모리에서 해제하는 부분을 잘 체크해야 합니다.

그러니까, 위의 예제에서는 이렇게 보실 수 있습니다.
interface UserFoundListener {
    fun onUserFound(userInfo: UserInfoItemModel?)
}

getUserNickname(object: UserFoundListener() {
    override fun onUserFound(userInfo: UserInfoItemModel?) {
        ...
    }
})

자바 같으면 이런 식으로 인터페이스의 인스턴스를 넘겼을 텐데, 코틀린에서는 UserFoundListener 안에 있는 onUserFound 함수의 정의 형태만(파라미터 타입과 리턴타입) 넘길 수 있도록 지원하는 겁니다. 파라미터 이름은 인터페이스 내의 함수와 동일하지 않아도 상관없습니다.
getUserNickname(onUserFound: (UserInfoItemModel?) -> Unit)

그리고 안드로이는 기본이 비동기식 처리라고 보시면 됩니다. 멀티 스레드 환경이라고도 보실 수 있구요. 따라서 멀티 스레드와 비동기처리를 항상 염두에 두고 프로그램을 짜야 합니다.
...