본문 바로가기
안드로이드 코틀린

[Kotlin][Android] ViewModel을 이용한 타이머 만들기.

by teamnova 2021. 11. 16.

안녕하세요. 이번 시간에는 App Architcture Component에서 지원하고 있는 라이브러리를 이용해서 리사이클러뷰를 구현해 볼려고 합니다.

 

App Architecture Componenet에는 ViewModel, LiveData, lifecycle등이 있는데요. 각 요소들을 왜 써야 하는지 공홈을 통해 확인해 보도록 하겠습니다.

 

우선 첫번째로 Lifecycle에 대해 이해가 필요합니다. Lifecycle package가 왜 나왔을까요? 

 

안드로이드는 Activity가 프로세스의 진입점으로 각 액티비티가 독립적으로 실행될 수 있는 단위가 됩니다. 그렇다 보니 Activity가 더 이상 사용하지 않을 때 Acitivty에 사용한 자원들을 해제 해줘야 모바일의 제한된 물리적 자원을 효율적으로 사용할 수 있겠죠? 

 

그래서 안드로이드는 이를 Activity마다 생명주기(lifecycle)을 주어서 해결하도록 하였습니다. 이렇게 문제가 해결된듯 보였지만 앱을 만들다가 보면 발생되는 문제가 있었습니다.

 

그것은 바로 lifecycle의 method에 코드과 점점 집중되는 문제가 생기게 된 것입니다. 코드가 집중되면 우선 코드를 파악하기가 힘들고 그리고 서로 영향을 미칠 수 있기 때문에 최대한 역할에 맞춰서 독립적으로 돌아 갈 수 있도록 하는게 좋습니다. 

 

이를 해결해줄 수 있는게 바로 Lifecycle Package에 있는 요소들입니다. 

https://developer.android.com/reference/androidx/lifecycle/package-summary

 

androidx.lifecycle  |  Android Developers

Content and code samples on this page are subject to the licenses described in the Content License. Java is a registered trademark of Oracle and/or its affiliates. Last updated 2021-08-18 UTC. [{ "type": "thumb-down", "id": "missingTheInformationINeed", "l

developer.android.com

우리가 알고 있는  ViewModel이 바로 이 패키지 안에 있는 Class들입니다. 그러므로 LiveData나 ViewModel을 사용하는 이유는 바로 Lifecycle을 다루기 위함이라고 볼 수 있는 것이죠.

 

그럼 조금 더 구체적으로 ViewModel 과 LiveData를 왜 써야 하는지 설명드리도록 하겠습니다.

 

ViewModel은 이름에서 확인할 수 있듯이 View와 관련된 데이터를 관리하는 Model이라고 볼 수 있습니다. 왜 Acitvity나 Fragment에서 데이터를 관리하지 않고 ViewModel에서 관리하는게 좋을까요? 왜나하면 Activity나 Fragment는 생명주기가 너무 짧은데 문제가 있습니다. 사용자가 만약 화면을 세로에서 가로로 전환을 하게 되면 Activity의 생명주기가 다시 돌게 되는데요. 그렇게 되면 사용자가 가지고 있는 데이터를 처음부터 다시 받아와야 합니다. 그러면 사용중에 있는 데이터, 예로 들면 타이머 같은 경우에는 기존 값을 저장하지 않았다면 0초가 되는 문제점이 있습니다. 그래서 이보다 더 지속 가능한 Component가 필요한데 그게 바로 ViewModel이라고 할 수 있습니다.

 

더 자세한 내용이 궁금하시면 아래 ViewModel 글에 대해서 읽어 보시면 좋을 거 같습니다.

https://developer.android.com/topic/libraries/architecture/viewmodel

 

ViewModel 개요  |  Android 개발자  |  Android Developers

ViewModel을 사용하면 수명 주기를 인식하는 방식으로 UI 데이터를 관리할 수 있습니다.

developer.android.com

 

그래서 제가 이번 시간에 만들고자 하는 것은 화면 전환이 이루어져도 시간이 기존대로 지속되는 것을 타이머를 이용해서 구현해보고자 합니다.

 

Layout은 다음과 같이 간단하게 만들어보았습니다.

<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/activity_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context="com.codechacha.sample.step2.ChronoActivity2">

    <Chronometer
            android:id="@+id/chronometer"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textSize="60sp"
            android:layout_centerHorizontal="true"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

화면 결과

Acitivty 코드

class ChronoActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chrono_2)

        // ViewModel 초기화
        val chronometerViewModel = ViewModelProviders.of(this).get(ChronometerViewModel::class.java)

        if (chronometerViewModel.getStartTime() <= 0) {
            Logger.d(" ViewModel에 기존 시간초 데이터가 업을 경우, 타이머 실행 & ViewModel의 데이터를 현재 값으로 저장")
            val startTime = SystemClock.elapsedRealtime()
            chronometerViewModel.setStartTime(startTime)
            chronometer.base = startTime
        } else {
            Logger.d(" ViewModel에 데이터가 있는 경우, 저장된 값으로 타이머 초기화")
            chronometer.base = chronometerViewModel.getStartTime()
        }
        chronometer.start()
    }
}

 

ViewModel 코드

class ChronometerViewModel : ViewModel() {
    private var startTime: Long = 0

    fun getStartTime(): Long = startTime

    fun setStartTime(startTime: Long) {
        this.startTime = startTime
    }
}

 

코드안에 있는 Logger은 밑의 스틱코드를 이용하면 쉽게 불러와서 사용할 수 있습니다.

https://stickode.com/detail.html?no=2559 

 

스틱코드

 

stickode.com