[Android] 기억에 남는 장소 저장 앱 개발기 - 4

송규빈·2022년 8월 6일
0

DayToGo 개발기

목록 보기
4/7
post-custom-banner

ViewModel

개발을 하다 문득 생각이 들었다.
근데 왜 ViewModel은 application을 가지는 AndroidViewModel이 있는데, activity 단의 Context를 가지면 안 될까?
그 이유를 계속 곱씹어봤다. ViewModel에서 context 객체를 저장하지 말아야 하는 이유는 ViewModel이 특정 UI 컨트롤러 인스턴스보다 오래 지속되기 때문이다.
화면 회전을 예로 들면, 화면 회전을 세 번하면 Activity는 세 개의 다른 Activity 인스턴스를 생성하지만 ViewModel은 하나만 갖고 있기 때문이다.
그러면 application은?? application context는 당연히도 application의 생명주기와 연관되어 있기 때문에 괜찮다.
그렇다고 해서 activitiy context를 인자로 넘겨야하는 상황에 application context를 넘기면 메모리 누수가 발생하기 때문에 지양해야 한다.

로그인 중복 클릭 방지

파이어베이스 로그인을 구현하는 과정에서 많은 수정이 있었다.
제일 컸던 건 중복 클릭 방지였다.
View에서는 파이어베이스 로그인을 통해 받아온 user 정보를 관찰(Observing)하고 user정보가 들어왔다면 로그인이 완료되었다는 것으로 판단하여 다음 화면으로 넘어간다.
여기까지는 문제가 없었지만, 짧은 시간내에 로그인 버튼을 여러 번 클릭하였을 때 수많은 액티비티가 뜬다는 점이었다.
그래서 구글링을 하여 중복 방지 솔루션을 얻었다.
이번 프로젝트에서는 그리 복잡한 api호출은 없었지만 그래도 써보고 싶어서 RxJava로 구현하였다. 그래서 RxJava의 throttleFirst를 사용하여 첫번째 클릭 이벤트만 내보내도록 하였으나 문제가 또 있었다.
하지만 '중복 방지' 하나만을 위해 구독을 취소하는 과정은 내 생각엔 그렇게 효율적이지 않았다.
그러던 도중 구글에서 제안한 SingleLiveData를 사용하는 방식을 보았고 중복 방지를 막았다.
하지만... 또 다른 문제가 있었다. "파이어베이스 로그인을 누른 뒤 카카오 로그인이 하고 싶어서 빠르게 카카오 로그인을 누른다면?"
SingleLiveData를 사용한다면 단일 뷰에 대해서만 중복 방지를 막을 수 있었기에 전체 뷰를 막을 수 있는 방법이 필요했다.
그래서 또 다시 여러 문서들과 블로그를 찾아 본 결과 Event클래스를 따로 만들어 handling하는 방법이었다.

open class Event<out T>(private val content: T) {


    var hasBeenHandled = false
        private set // Allow external read but not write

    /**
     * Returns the content and prevents its use again.
     */
    fun getContentIfNotHandled(): T? {
        val currentClickTime = SystemClock.uptimeMillis()
        val elapsedTime: Long = currentClickTime - lastClickTime
        lastClickTime = currentClickTime

        if (elapsedTime <= MIN_CLICK_INTERVAL) {
            return null
        }

        return if (hasBeenHandled) {
            null
        } else {
            hasBeenHandled = true
            content
        }
    }

    /**
     * Returns the content, even if it's already been handled.
     */
    fun peekContent(): T = content

    companion object {
        private val MIN_CLICK_INTERVAL: Long = 1000
        private var lastClickTime: Long = 0
    }

}

참고 1, 참고 2

profile
🚀 상상을 좋아하는 개발자
post-custom-banner

0개의 댓글