본문 바로가기

쉽게 읽는 개발문서/안드로이드 개발문서

간단하게 ViewModel을 알아보자 - Incorporate Lifecycle-Aware Components 2021-07-01

해당 게시글은 코드랩 예제를 참고하였습니다.

 

먼저 step1의 경우 chronometer를 그대로 실행하여 타이머를 구성하였기에 따로 바꿀 필요 없이 
앱 코드만 다운로드해서 실행만 하면 됩니다:) (밑에는 샘플코드를 다운로드 할 수 있는 링크입니다.)
https://developer.android.com/codelabs/android-lifecycles?hl=ko#1

 

Incorporate Lifecycle-Aware Components  |  Android 개발자

In this codelab, you'll learn about lifecycle-aware architecture components and how to incorporate LiveData and ViewModels into your app.

developer.android.com

3. Step 2 - Add a ViewModel

해당 예제에서는 chronometer 뷰 컴포넌트를 이용하여 타이머를 만들었는데, ViewModel을 사용하면 화면이 회전도기너, 액티비티가 소멸되어도 타이머가 리셋되지 않는다고 나와있습니다 :)

전체 소스코드를 먼저 봐보죠!! :)

[build.gradle(Module)]

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'kotlin-kapt'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.devjamesp.architecture"
        minSdkVersion 23
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    kotlinOptions {
        jvmTarget = '1.8'
    }
    buildFeatures {
        viewBinding = true
        dataBinding = true
    }
}

dependencies {

    implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
    implementation 'androidx.core:core-ktx:1.5.0'
    implementation 'androidx.appcompat:appcompat:1.3.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

    // test lib
    testImplementation 'junit:junit:4.13.2'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'

    implementation ("androidx.activity:activity-ktx:1.2.3")
    implementation ("androidx.fragment:fragment-ktx:1.3.5")
    implementation ("androidx.cardview:cardview:1.0.0")
    implementation ("androidx.recyclerview:recyclerview:1.2.1")
    implementation ("androidx.room:room-ktx:2.3.0")
    implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1")

    // ViewModel
    implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
}

buildFeatures { ... } 부분은 일일이 view를 findViewById로 바인딩 하는게 귀찮아서 추가한 부분입니다.
이부분은 제외하시고 findViewById로 chronometer뷰를 할당하셔서 사용하셔도 됩니다:)

 

[ChronometerViewModel.kt]

package com.devjamesp.architecture.step2

import androidx.lifecycle.ViewModel

// data 관리는 여기서~
class ChronometerViewModel : ViewModel() {
    var mStartTime : Long = 0L
}

 

[MainActivity.kt]

package com.devjamesp.architecture.step2

import android.os.Bundle
import android.os.SystemClock
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import com.devjamesp.architecture.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
    private lateinit var binding : ActivityMainBinding
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // viewModel 생성
       val chronometerViewModel = ViewModelProvider(this).get(ChronometerViewModel::class.java)

        if(chronometerViewModel.mStartTime == 0L) {
            val startTime =  SystemClock.elapsedRealtime()
            chronometerViewModel.mStartTime = startTime
            binding.chronometer.base = startTime // viewModel -> view data전달
        } else {
            binding.chronometer.base = chronometerViewModel.mStartTime
        }

        binding.chronometer.start()
    }
}

 

[activity_main.xml]

<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".step2.MainActivity"
    android:padding="16dp">

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toBottomOf="@id/chronometer"/>


</androidx.constraintlayout.widget.ConstraintLayout>

 

코드랩 예제 코드가 너무 잘나와있어서 쉽게 할 수 있습니다.

핵심은 해당 코드입니다.

뷰모델을 생성하는 코드입니다. 생성된 객체를 activity에서 가지고 있고, VIewModel객체에는 
mStartTime이라는 프로퍼티가 있고, 이를 통해 

처음 mStartTime값이 0일때, 현재 시간을 측정하여, ViewModel과 최초 한번만 동기화해줍니다.
그리고나서 view에 시간 데이터를 전달하여 사용자에게 보여줍니다.

그리고 0이 아니라면, view에 바로 전달하면 되겠죠? :)
(이유는 액티비티가 소멸되는 경우viewModel은 살아있기 때문에 초기화를 또 해줄 필요가 없기 때문입니다.

chronometer.start()메소드를 호출하여 시작하면 끝~

[실행 화면]

 

AppCompatActivity를 포함한 액티비티클래스들은 ComponentActivity 클래스를 상속하고 있는데 이 클래스가 LifecycleOwner 인터페이스를 구현하고 있고,  lifecycleOwner의 scope가 유지되고 있는 한 ViewModel은 유지된다고 합니다.
(다음의 경우 ViewModel은 죽지 않는다고 하네요:)
1. 스크린이 회전하는 경우
2. 또다른 앱을 실행하거나 기존의 다른 앱으로 돌아가는 경우

이상 간단하게 ViewModel을 사용하는 방법을 알아봤습니다. :)

https://developer.android.com/codelabs/android-lifecycles?hl=ko#0 

 

Incorporate Lifecycle-Aware Components  |  Android 개발자

In this codelab, you'll learn about lifecycle-aware architecture components and how to incorporate LiveData and ViewModels into your app.

developer.android.com