[Android] AAC, MVVM

leeehaยท2022๋…„ 12์›” 26์ผ
0
post-thumbnail

AAC (Android Architecture Components)

  • 2017 ๊ตฌ๊ธ€ I/O์—์„œ ๋ฐœํ‘œ
  • 2018 ๊ตฌ๊ธ€ I/O์—์„œ ๋ฐœํ‘œ๋œ Jetpack์œผ๋กœ ํ†ตํ•ฉ
  • ์•„ํ‚คํ…์ฒ˜ ์ปดํฌ๋„ŒํŠธ๊ฐ€ ๊ณต๊ฐœ๋˜๊ธฐ ์ „๊นŒ์ง€๋Š” ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์—์„œ ํŠน์ • ์•„ํ‚คํ…์ฒ˜๊ฐ€ ๊ถŒ์žฅ๋˜์ง€ ์•Š์•˜๋‹ค.
  • ๊ฐœ๋ฐœ์ž๋“ค์— ๋”ฐ๋ผ์„œ MVP, MVC, MVVM, MVPP ๋“ฑ ๋‹ค์–‘ํ•œ ์•„ํ‚คํ…์ฒ˜๊ฐ€ ์„ ํƒ๋˜์—ˆ๋‹ค.
  • ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์„ ์œ„ํ•œ ์•„ํ‚คํ…์ฒ˜๋ฅผ ์ •์˜ํ•˜๊ณ  ์ด๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ œ๊ณตํ•˜๊ณ ์ž ๊ตฌ๊ธ€์—์„œ ์•„ํ‚คํ…์ฒ˜ ์ปดํฌ๋„ŒํŠธ๋ฅผ ๊ณต๊ฐœํ•˜์˜€๋‹ค.

DataBinding

  • ๋ฐ์ดํ„ฐ๋ฅผ UI์— ์‰ฝ๊ฒŒ ๋ฐ”์ธ๋”ฉํ•˜๊ธฐ ์œ„ํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์ธ๋ฐ, ๋ทฐ์™€ ๊ด€๋ จ๋œ ์ฝ”๋“œ๋Š” ๋ชจ๋‘ xml์—์„œ ์ž‘์„ฑํ•œ๋‹ค๋Š” ์ ์—์„œ ์•„ํ‚คํ…์ฒ˜ ์š”์†Œ๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ๋‹ค.
  • findViewById()์— ์˜ํ•œ ๋ทฐ ๊ฐ์ฒด ํš๋“ ๋ฐ ์ด์šฉ์˜ ๋ฒˆ๊ฑฐ๋กœ์›€์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•œ ๋ชฉ์ 
  • Butterknife ๋“ฑ์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ด์šฉ๋ณด๋‹ค ํšจ์œจ์ 
  • ์•ˆ๋“œ๋กœ์ด๋“œ ์•„ํ‚คํ…์ฒ˜ ์ปดํฌ๋„ŒํŠธ์˜ ๊ตฌ์„ฑ์š”์†Œ
<layout>
  <data>
  	<variable name="model1" type="com.example.test_databinding.User" />
  </data>
  
  <TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text='@{"i am include xml.. binding data : " + model1.name}' />
</layout>

Lifecycle Aware Components

  • ์•กํ‹ฐ๋น„ํ‹ฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ์˜ ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ์˜ต์ €๋ฒ„์—์„œ ๋”ฐ๋กœ ์ฒ˜๋ฆฌํ•˜๋„๋ก ์„ค๊ณ„
  • ์ƒ๋ช…์ฃผ๊ธฐ๋ฅผ ๊ฐ–๋Š” Owner์™€ ์ด๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋‹ค๊ฐ€ ์ฒ˜๋ฆฌํ•˜๋Š” Observer

  • ํ™”๋ฉด ๊ฐ„ ์ด๋™์„ ๊ฐ„ํŽธํ•˜๊ฒŒ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ฃผ๋Š” Jetpack์˜ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ํ˜น์€ ํˆด
  • ํ•˜๋‚˜์˜ ๋ฉ”์ธ ์•กํ‹ฐ๋น„ํ‹ฐ์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๋กœ ํ™”๋ฉด์„ ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉ์ ์œผ๋กœ ํ•œ๋‹ค.

Room

  • SQLite๋ฅผ ์‚ฌ์šฉํ•จ์— ์žˆ์–ด ๋ณ„๋„์˜ Query๋ฌธ ์ž‘์„ฑ์—†์ด ๊ฐ„ํŽธํ•˜๊ฒŒ Insert, Delete ๋“ฑ์˜ ๋™์ž‘์„ ํ•  ์ˆ˜ ์žˆ๊ฒŒ๋” ๋„์™€์ฃผ๋Š” ORM ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • ์•ˆ๋“œ๋กœ์ด๋“œ ์•ฑ์—์„œ ์‚ฌ์šฉํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ Room์œผ๋กœ ๊ถŒ๊ณ 

Paging

  • ๋ฆฌ์‚ฌ์ดํด๋Ÿฌ๋ทฐ์—์„œ ํŽ˜์ด์ง• ์ฒ˜๋ฆฌ๋ฅผ ์‰ฝ๊ณ  ํšจ์œจ์ ์œผ๋กœ ์ž‘์„ฑํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ
  • ํŽ˜์ด์ง• ๋œ ๋ฐ์ดํ„ฐ๋Š” ๋ฉ”๋ชจ๋ฆฌ ๋‚ด์— ์บ์‹ฑ๋œ๋‹ค.
  • ๋ฐ์ดํ„ฐ ์š”์ฒญ ๋ฐ ์ค‘๋ณต ์ œ๊ฑฐ ๊ธฐ๋Šฅ์ด ๊ธฐ๋ณธ์ ์œผ๋กœ ์ œ๊ณต๋œ๋‹ค.
  • ๋ฐ์ดํ„ฐ์˜ ๋๊นŒ์ง€ ์Šคํฌ๋กค์„ ํ•˜๋ฉด, ์–ด๋Œ‘ํ„ฐ๊ฐ€ ์ž๋™์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์š”์ฒญํ•œ๋‹ค.

WorkManager

  • WorkManager๋Š” ํŠน์ • ์ž‘์—…์˜ ์œ ์˜ˆ (Defer) ์ฒ˜๋ฆฌ ๋ฐ ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๋ฐฉ๋ฒ•์„ ์ œ๊ณตํ•œ๋‹ค.
  • JobScheduler, FirebaseJobDispatcher, AlarmManager ๋“ฑ์„ ํ†ตํ•ฉํ•œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ

MVC vs MVVM

์ฐธ๊ณ ํ•œ ํฌ์ŠคํŒ…

MVC

  1. Controller๊ฐ€ ์‚ฌ์šฉ์ž ๋™์ž‘์„ ๋ฐ›์•„๋“ค์ž„. (ํ…์ŠคํŠธ ์ž…๋ ฅ, ๋ฒ„ํŠผ ํ„ฐ์น˜ ๋“ฑ)
  2. Controller๊ฐ€ ์‚ฌ์šฉ์ž์˜ ๋™์ž‘์— ๋”ฐ๋ฅธ Model ์—…๋ฐ์ดํŠธ๋ฅผ ์š”์ฒญํ•จ.
  3. Controller๊ฐ€ Model์„ ๋‚˜ํƒ€๋‚ผ View๋ฅผ ์„ ํƒํ•จ.
  4. View๋Š” Model์„ ์ฐธ์กฐํ•˜์—ฌ UI๋ฅผ ์—…๋ฐ์ดํŠธํ•จ.

MVC์˜ ๋‹จ์ 

  • ์ž์—ฐ์Šค๋ ˆ View์™€ Model ๊ฐ„์˜ ์˜์กด์„ฑ์ด ๋†’์•„์ง„๋‹ค.
  • ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ Model๊ณผ View ์‚ฌ์ด์—์„œ ๋ฐ”์˜๊ฒŒ ์›€์ง์ด๊ณ  ์žˆ๋‹ค. (ํ˜ผ์ž์„œ ์—ฌ๊ธฐ์ €๊ธฐ ์š”์ฒญ์„ ๋ณด๋‚ด์•ผ ํ•˜๋Š” ์ปจํŠธ๋กค๋Ÿฌ์˜ ๋™์ž‘์€ ๋‹น์—ฐํžˆ ๋ฌด๊ฑฐ์›Œ์งˆ ์ˆ˜๋ฐ–์— ์—†๋‹ค.)
  • ๋”ฐ๋ผ์„œ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ์œ„ํ•ด ์ฝ”๋“œ๋ฅผ ์ž˜๋ชป ๊ฑด๋“œ๋ ธ๋‹ค๊ฐ€, UI ํ”„๋ ˆ์ž„ ์Šคํ‚ต ํ˜„์ƒ์ด๋‚˜ ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฐ™์€ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ์ด์ฒ˜๋Ÿผ MVC ํŒจํ„ด์€ ๊ตฌํ˜„ํ•˜๊ธฐ ์‰ฌ์šด ์žฅ์ ์ด ์žˆ๋Š” ๋ฐ˜๋ฉด์—, ๊ธฐ๋Šฅ ์ถ”๊ฐ€ ๋ฐ ๋ณ€๊ฒฝ์— ์žˆ์–ด์„œ ์œ ์ง€๋ณด์ˆ˜๊ฐ€ ์–ด๋ ต๋‹ค.

MVVM

  • MVC์˜ ๋‹จ์ ์„ ๋ณด์™„ํ•˜๊ณ ์ž ๋“ฑ์žฅํ•œ ๋””์ž์ธ ํŒจํ„ด
  • ๊ธฐ์กด์˜ MVC์—์„œ ์ปจํŠธ๋กค๋Ÿฌ์—๊ฒŒ ๋ง‰์ค‘ํ•œ ์—ญํ• ์„ ๋ถ€์—ฌํ•˜๊ธฐ๋ณด๋‹ค, View์™€ Model์˜ ๋™์ž‘ ์ž์ฒด๋ฅผ ๋ถ„๋ฆฌํ•˜์—ฌ ์œ ์ง€๋ณด์ˆ˜๋ฅผ ํŽธ๋ฆฌํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋„๋ก ๋„์™€์ค€๋‹ค.

View

  • Activity/Fragment๊ฐ€ View ์—ญํ• ์„ ํ•จ.
  • ์‚ฌ์šฉ์ž์˜ ๋™์ž‘์„ ๋ฐ›์•„๋“ค์ž„.
  • ViewModel์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐํ•˜์—ฌ UI ๊ฐฑ์‹ 

ViewModel

  • View๊ฐ€ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ๋ฅผ Model์— ์š”์ฒญํ•จ.
  • Model๋กœ๋ถ€ํ„ฐ ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์Œ.

Model

  • ViewModel์ด ์š”์ฒญํ•œ ๋ฐ์ดํ„ฐ ๋ฐ˜ํ™˜
  • Room, Realm๊ณผ ๊ฐ™์€ DB ์‚ฌ์šฉ์ด๋‚˜ Retrofit์„ ํ†ตํ•œ ๋ฐฑ์—”๋“œ API ํ˜ธ์ถœ (๋„คํŠธ์›Œํ‚น)์ด ๋ณดํŽธ์ 

MVVM์˜ ์žฅ์ 

  • View๋Š” ViewModel์ด ์ฅ๊ณ  ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐํ•œ๋‹ค. ๋”ฐ๋ผ์„œ MVC ํŒจํ„ด์ฒ˜๋Ÿผ View๊ฐ€ Model์— ์ง์ ‘ ์ ‘๊ทผํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— View๋Š” UI ๊ฐฑ์‹ ์—๋งŒ ์ง‘์ค‘ํ•  ์ˆ˜ ์žˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋ฐ์ดํ„ฐ๋ฅผ ๊ด€์ฐฐํ•˜๊ณ  ์žˆ๋Š” ๋งŒํผ ๋ฐ์ดํ„ฐ์˜ ๋ณ€ํ™”์— ๋”์šฑ ๋Šฅ๋™์ ์œผ๋กœ ์›€์ง์ด๊ฒŒ ๋œ๋‹ค.
  • ViewModel์ด ๋ฐ์ดํ„ฐ๋ฅผ ์ฅ๊ณ  ์žˆ์œผ๋ฏ€๋กœ, ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜ ๊ฐ€๋Šฅ์„ฑ ๋ฐฐ์ œ (๋ฐ์ดํ„ฐ๊ฐ€ Activity/Fragment์™€ ๊ฐ™์€ View์˜ ๋ผ์ดํ”„์‚ฌ์ดํด์— ์˜์กดํ•˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—)
  • ๊ธฐ๋Šฅ๋ณ„ ๋ชจ๋“ˆํ™”๊ฐ€ ์ž˜ ๋˜์–ด ์žˆ์–ด ์œ ์ง€๋ณด์ˆ˜์— ์šฉ์ดํ•˜๋‹ค. (ViewModel ์žฌ์‚ฌ์šฉ, DB ๊ต์ฒด ๋“ฑ์˜ ์ž‘์—…์ด ํŽธ๋ฆฌํ•ด์ง„๋‹ค.)

MVVM์„ ์ ์šฉํ•œ AAC

Activity/Fragment: View

  • UI๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ  ์œ ์ €์™€ ์ƒํ˜ธ์ž‘์šฉ ํ•˜๋Š” ๋ถ€๋ถ„ (์ƒ๋ช…์ฃผ๊ธฐ์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€)
  • ์ƒ๋ช…์ฃผ๊ธฐ์˜ ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•˜๋ฉด ViewModel์— ๋ฐ์ดํ„ฐ ๊ฐฑ์‹  ์š”์ฒญ
  • ViewModel์—์„œ ์ „๋‹ฌํ•œ LiveData์˜ ๋‚ด์šฉ์„ ํ™”๋ฉด์— ์ถœ๋ ฅ

ViewModel: View์™€ Model์˜ ๋ถ„๋ฆฌ

  • View์™€ Model์„ ๋ถ„๋ฆฌ์‹œํ‚ค๊ธฐ ์œ„ํ•œ ๊ฐ€๊ต (์ž„์‹œ ๋‹ค๋ฆฌ) ์—ญํ• 
  • View์˜ ์ƒ๋ช…์ฃผ๊ธฐ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๊ณ  ๋ณ€๊ฒฝ์ด ๋ฐœ์ƒํ•˜๋ฉด Repository๋ฅผ ์‹คํ–‰
  • Repository์—์„œ ๋ฐœ์ƒํ•œ LiveData๋ฅผ View์— ์ „๋‹ฌ

Repository: ๋ฐ์ดํ„ฐ ์ฒ˜๋ฆฌ (Model)

  • ๋ฐ์ดํ„ฐ์˜ ์ €์žฅ ๋ฐ ํš๋“์ด ์ฃผ๋ชฉ์ 
  • SQLite์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋Š” Room, ๋„คํŠธ์›Œํฌ ํ†ต์‹ ์œผ๋กœ ์„œ๋ฒ„๋กœ๋ถ€ํ„ฐ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๋•Œ๋Š” Retrofit์„ ์ฃผ๋กœ ์‚ฌ์šฉ

ViewModel

  • MVVM์˜ ํ•ต์‹ฌ์€ ํ™”๋ฉด (View)์—์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง (Model)์„ ๋ถ„๋ฆฌํ•˜์—ฌ ๊ฐœ๋ฐœํ•˜๋Š” ๊ฒƒ
  • Activity, Fragment์—์„œ UI๋ฅผ ์ฒ˜๋ฆฌํ•˜๊ณ  ViewModel์—์„œ ๋น„์ฆˆ๋‹ˆ์Šค ๋กœ์ง์„ ์ฒ˜๋ฆฌ
  • Activity์—์„œ onSaveInstanceState() ํ•จ์ˆ˜๋ฅผ ์ด์šฉํ•ด Bundle์— ์•กํ‹ฐ๋น„ํ‹ฐ์˜ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๋Š” ๊ฒƒ๋„ ViewModel๋กœ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ๋‹ค.

์‚ฌ์šฉ ๋ฐฉ๋ฒ•

  • build.gradle ํŒŒ์ผ์—์„œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ dependency ์„ค์ •

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1

  • ViewModel ํด๋ž˜์Šค๋ฅผ ์ƒ์† ๋ฐ›์•„์„œ ์ž‘์„ฑ
class MyViewModel: ViewModel() {
	val user: MutableLiveData<User>
  		get() {
        	// LiveData์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ 
    		val user = MutableLiveData<User>()
    		user.postValue(User("gildong", "hong1"))
    		return user
  		}
}

์•กํ‹ฐ๋น„ํ‹ฐ์—์„œ ViewModel์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

  • ViewModel ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•  ์ˆ˜๋„ ์žˆ์œผ๋‚˜, ViewModelProvider๋ฅผ ์ด์šฉํ•˜๋Š” ๊ฒŒ ๊ถŒ์žฅ๋œ๋‹ค.
  • ์™œ๋ƒํ•˜๋ฉด, ViewModel์˜ ์ด์  ์ค‘ ํ•˜๋‚˜๊ฐ€ ์•กํ‹ฐ๋น„ํ‹ฐ์˜ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋ฅผ Bundle์„ ์ด์šฉํ•˜์ง€ ์•Š๊ณ ์„œ๋„ ์‰ฝ๊ฒŒ ์ €์žฅํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฑด๋ฐ, ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๋ฉด ์ด ์ด์ ์ด ์‚ฌ๋ผ์ง„๋‹ค.
  • ViewModelProvider๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์•ผ ViewModel์˜ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ์•กํ‹ฐ๋น„ํ‹ฐ์™€ ๋‹ค๋ฅด๊ฒŒ ์›€์ง์ธ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ, ๊ฐ์ฒด๋ฅผ ์ง์ ‘ ์ƒ์„ฑํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ViewModel์˜ ์ƒ๋ช…์ฃผ๊ธฐ๊ฐ€ ์•กํ‹ฐ๋น„ํ‹ฐ์™€ ๋™์ผํ•˜๊ฒŒ ์›€์ง์ด๋ฏ€๋กœ, ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์†Œ๋ฉธ๋  ๋•Œ ๋ทฐ๋ชจ๋ธ๋„ ๊ฐ™์ด ์†Œ๋ฉธ๋˜์–ด ์•กํ‹ฐ๋น„ํ‹ฐ์˜ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ์ˆ˜ ์—†๋‹ค. ๋˜๋Š” ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์ƒ์„ฑ๋  ๋•Œ ๋ทฐ๋ชจ๋ธ์ด ๋‹ค์‹œ ์ƒ์„ฑ๋˜๋Š” ๋ฌธ์ œ๋„ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
val model = ViewModelProvider(this).get(MyViewModel::class.java)

model.user.observe(this, {
	binding.textView.text = "${it.firstName}, ${it.lastName}"
})
  • kotlin property delegator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ฐ„๊ฒฐํ•œ ์ฝ”๋“œ๋กœ ๋ทฐ๋ชจ๋ธ ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•  ์ˆ˜๋„ ์žˆ๋‹ค.

implementation "androidx.activity:activity-ktx:$activity_version"

val model: MyViewModel by viewModels()
  • ๋ทฐ๋ชจ๋ธ ๋‚ด์—์„œ ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ์ฒด๋ฅผ ํ•„์š”๋กœ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ViewModel์˜ ์„œ๋ธŒ ํด๋ž˜์Šค์ธ AndroidViewModel์„ ์‚ฌ์šฉํ•˜๋ฉด ๋œ๋‹ค.
  • ViewModel() : ๊ธฐ๋ณธ์ ์œผ๋กœ๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜ ์—†์Œ.
  • AndroidViewModel(Application application) : ์–ดํ”Œ๋ฆฌ์ผ€์ด์…˜ ๊ฐ์ฒด๋ฅผ ๋ทฐ๋ชจ๋ธ์— ๋„˜๊ฒจ์คŒ.

ViewModel์˜ ์ƒ๋ช…์ฃผ๊ธฐ

  • ํ™”๋ฉด ํšŒ์ „์— ์˜ํ•ด ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ์†Œ๋ฉธ๋œ๋‹ค๊ณ  ํ•ด๋„ ๊ทธ ์ƒํƒœ ๋ฐ์ดํ„ฐ๋Š” ViewModel์— ์—ฌ์ „ํžˆ ์ €์žฅ๋˜์–ด์žˆ๋‹ค. ๊ทธ๋ž˜์„œ ์•กํ‹ฐ๋น„ํ‹ฐ๊ฐ€ ๋‹ค์‹œ ์žฌ๊ฐœ๋˜๋ฉด ๊ทธ ๋ฐ์ดํ„ฐ๋ฅผ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ํ”„๋ž˜๊ทธ๋จผํŠธ์—์„œ ViewModel์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

  • ์•กํ‹ฐ๋น„ํ‹ฐ ๋‚ด์— ์—ฌ๋Ÿฌ ๊ฐœ์˜ ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ, ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ฐ„์— ๋ทฐ๋ชจ๋ธ์„ ๊ณต์œ ํ•ด์•ผ ํ•  ๋•Œ๊ฐ€ ์žˆ๋‹ค.

  • ViewModel์˜ Lifecycle Owner๋ฅผ ์•กํ‹ฐ๋น„ํ‹ฐ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด, ์—ฌ๋Ÿฌ ํ”„๋ž˜๊ทธ๋จผํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ๋ทฐ๋ชจ๋ธ์„ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ViewModelProvider์˜ ๋งค๊ฐœ๋ณ€์ˆ˜๋กœ Lifecycle Owner๋ฅผ ์ง€์ •ํ•˜๋ฉด ๋œ๋‹ค.
// ํ˜„์žฌ ์•กํ‹ฐ๋น„ํ‹ฐ๋ฅผ Lifecycle Owner๋กœ ์„ค์ • (ํ”„๋ž˜๊ทธ๋จผํŠธ ๊ฐ„์— ๋ทฐ๋ชจ๋ธ ๊ณต์œ ) 
val model = ViewModelProvider(requireActivity()).get(MyFragmentViewModel::class.java)

LiveData

  • ViewModel์˜ ๊ฒฐ๊ณผ๋ฅผ View์— ๋„˜๊ธธ ๋•Œ, LiveData ํƒ€์ž…์œผ๋กœ ๋„˜๊ธด๋‹ค.
  • ViewModel์—์„œ String ๋“ฑ์˜ ํƒ€์ž…์œผ๋กœ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํ„ดํ•  ์ˆ˜๋„ ์žˆ์ง€๋งŒ, LivaData ํƒ€์ž…์œผ๋กœ ๋ฆฌํ„ดํ•ด์•ผ Observer๊ฐ€ ๊ทธ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ›์•„ ์ด์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • LiveData์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๊ณ  ์‹ถ์„ ๊ฒฝ์šฐ setValue(T) ๋ฐ postValue(T) ๋ฉ”์„œ๋“œ๋ฅผ ์ด์šฉํ•˜๋ฉด, LiveData๋ฅผ ๊ด€์ฐฐํ•˜๋Š” observer์—๊ฒŒ ์—…๋ฐ์ดํŠธ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ๋„˜๊ฒจ์ค€๋‹ค.
class MyViewModel: ViewModel() {
  fun someData(): String { 
  	// ์—ฌ๊ธฐ์„œ ์ž‘์—… ์ฒ˜๋ฆฌ๊ฐ€ ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋ฉด 
    // View๊ฐ€ ๊ฒฐ๊ณผ๊ฐ’์„ ๋ฐ›์„ ๋•Œ๊นŒ์ง€ ๊ณ„์† ๋Œ€๊ธฐํ•˜๋Š” ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  	return "hello"
  }
  
  // LiveData ํƒ€์ž…์œผ๋กœ ๋ฆฌํ„ดํ•˜๋ฉด ๋น„๋™๊ธฐ ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ 
  fun someData2(): MutableLiveData<String> {
    val liveData = MutableLiveData<String>()
    
    thread {
      SystemClock.sleep(3000)
      liveData.postValue("world") // 3์ดˆ ๋’ค์— ์‹คํ–‰ (View๋Š” ๊ณ„์† ๋Œ์•„๊ฐ€๊ณ  ์žˆ์Œ) 
    }
    
    return liveData // ์ผ๋‹จ ๊ฒฐ๊ณผ ๋ฆฌํ„ด
  }
}
  • LiveData์˜ ๋ณ€๊ฒฝ์„ ๊ฐ์ง€ํ•˜๋Š” Observer
  • LiveData์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด Observer์˜ onChanged() ํ•จ์ˆ˜๊ฐ€ ์ž๋™์œผ๋กœ ํ˜ธ์ถœ๋œ๋‹ค.
  • ๋” ์ด์ƒ ์ƒํƒœ ๋ณ€ํ™”๋ฅผ ๊ฐ์ง€ํ•  ํ•„์š”๊ฐ€ ์—†์–ด์ง€๋ฉด, ๋ช…์‹œ์ ์œผ๋กœ removeObservers() ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.
val observer = object: Observer<String> {
  override fun onChanged(t: String?) {
  	Log.d("kkang","onChanged......$t")
  }
}

// someData2 ํ•จ์ˆ˜๊ฐ€ ๋ฆฌํ„ดํ•˜๋Š” LiveData์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด 
// observer์˜ onChanged() ํ•จ์ˆ˜ ์‹คํ–‰ 
model.someData2().observe(this, observer)
val liveData = model.someData2()

// ...........................

liveData.removeObservers(this)
  • Custom LiveData๋Š” LiveData๋ฅผ ์ƒ์† ๋ฐ›์•„ ์ž‘์„ฑ
  • ViewModel ์ด์™ธ์— ๋‹ค๋ฅธ ๊ณณ์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
class MyLiveData : LiveData<String>() {
  fun sayHello(name: String) {
 	postValue("Hello $name")
  }
}
val liveData1 = MyLiveData()

// MyLiveData์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋˜๋ฉด ์ค‘๊ด„ํ˜ธ ๋ธ”๋Ÿญ ๋‚ด์šฉ ์‹คํ–‰ 
liveData1.observe(this) {
  Log.d("kkang", "result : $it")
}

liveData1.sayHello("kkang") 

์‹ค์Šต ์˜ˆ์ œ

implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1'
implementation 'androidx.activity:activity-ktx:1.2.3'
package com.tutorial.c100

import android.os.SystemClock
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import kotlin.concurrent.thread

class MyViewModel: ViewModel() {
    var sum = 0

    fun callSum(): MutableLiveData<String> {
        val liveData = MutableLiveData<String>()
        
        thread {
            for(i in 1..10){
                sum += i
                // LiveData์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ ์—…๋ฐ์ดํŠธ 
                // Observer์—๊ฒŒ ์—…๋ฐ์ดํŠธ ๋œ ๊ฐ’์„ ๋„˜๊ฒจ์ค€๋‹ค. 
                liveData.postValue(sum.toString())
                SystemClock.sleep(1000)
            }
        }

        return liveData
    }
}
package com.tutorial.c100

import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.tutorial.c100.databinding.ActivityMainBinding

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

        val model: MyViewModel by viewModels()

        binding.button.setOnClickListener {
        	// LiveData์˜ ๊ฐ’์ด ๋ณ€๊ฒฝ๋  ๋•Œ๋งˆ๋‹ค View์— ๋ฐ˜์˜ 
            model.callSum().observe(this) {
                binding.resultView.text = it
            }
        }
    }
}

profile
์Šต๊ด€์ด ๋  ๋•Œ๊นŒ์ง€ ๐Ÿ“

0๊ฐœ์˜ ๋Œ“๊ธ€