๐ SeSAC์ 'JetPack๊ณผ Kotlin์ ํ์ฉํ Android App ๊ฐ๋ฐ' ๊ฐ์ข๋ฅผ ์ ๋ฆฌํ ๊ธ ์ ๋๋ค.
2017 Google I/O ์์ ๋ฐํ
2018 Google I/O ์์ ๋ฐํ๋ JetPack ์ผ๋ก ํตํฉ
Architecture Components ๊ฐ ๊ณต๊ฐ๋๊ธฐ ์ ๊น์ง๋ ์๋๋ก์ด๋ ์ฑ์์ ํน์ Architecture ๊ฐ๊ถ์ฅ๋์ง ์์๋ค.
๊ฐ๋ฐ์๋ค์๊ฒ ๋ฐ๋ผ MVP, MVC, MVVM, MVPP ๋ฑ ๋ค์ํ ์ํคํ ์ฒ๊ฐ ์ ํ
์๋๋ก์ด๋ ์ฑ์ ์ํ ์ํคํ ์ฒ๋ฅผ ์ ์ํ๊ณ ์ด๋ฅผ ๊ตฌํํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ๊ณตํ๊ณ ์ architecture components ์ ๊ณต๊ฐ
๋ฐ๋ผ์ JetPack
์ ํฌ๊ฒ AAC
์ androidx
๋ก ๋๋๋ค๊ณ ๋ณผ ์ ์๊ฒ ๋ค. ์ด๊ธฐ ์๋๋ก์ด๋ ์ดํ๋ฆฌ์ผ์ด์
์ ๋จ์ํ ๊ตฌ์กฐ๋ฅผ ๊ฐ์ก์ง๋ง, ์ ์ ์๊ตฌํ๋ ๊ธฐ๋ฅ์ด ๋ง์์ง์ ๋ฐ๋ผ ๊ตฌ์กฐ๊ฐ ๋ณต์กํด์ก๋ค. ๊ทธ๋ฆฌ๊ณ ๊ตฌ๊ธ์ด ๋ฑํ ์ํคํ
์ฒ ๊ตฌ์กฐ
๋ก ์ง์ ํ ๊ฒ์ด ์์๊ธฐ ๋๋ฌธ์ ๊ฐ๋ฐ์๋ค์ ์ ๋ง๋ค ์์์ ์ํคํ
์ฒ๋ฅผ ์ ํํ์ฌ ์ฑ์ ๊ฐ๋ฐํด์๋ค. ๊ทธ๋ฌ๋ค ๋๋์ด ๊ตฌ๊ธ์์ ๊ถ์ฅ ์ํคํ
์ฒ๋ฅผ ๋ด๋์๊ณ , ์ด ์ํคํ
์ฒ์ ๊ตฌํ์ ์ง์ํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ๋ฐ๋ก AAC
๋ผ๊ณ ๋ณด๋ฉด ๋๊ฒ ๋ค.
<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>
Data ๋ฅผ UI ์ ์ฝ๊ฒ Binding ํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
findViewById() ์ ์ํ View ๊ฐ์ฒด ํ๋ ๋ฐ ์ด์ฉ์ ๋ฒ๊ฑฐ๋ก์์ ์ ๊ฑฐํ๊ธฐ ์ํ ๋ชฉ์
DI ๋ก ์ ๋ช ํ Butterknife ๋ฑ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ด์ฉ๋ณด๋ค ํจ์จ์
Android Architecture Component ์ ๊ตฌ์ฑ์์
findViewById ๋ก ๊ฐ์ฒด ํ๋ํ๋ ๊ฒ์ด ๋ฒ๊ฑฐ๋ก์ ๋์จ ๊ธฐ์ ์ด DataBinding
, ๊ทธ๋ฆฌ๊ณ DataBinding
์์ ๊ฐ์ฒด ํ๋ ์์๋ง ์ถ์ถํด์ ๋์จ ๊ฒ์ด ๋ฐ๋ก viewBindg
์ด๋ค. ๋๋ฌธ์ DataBinding ์ ์ข ๋ ์ํคํ
์ฒ ์์
์ ๊ฐ๊น๋ค๊ณ ๋ณผ ์ ์๊ฒ ๋ค. ๋ฐ์ดํฐ๋ ์ฝ๋์์ ๋จธ๋ฌผ๊ณ , ์ค์ UI ์ ์ฐํ๋ ๋ถ๋ถ์ XML ์์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ด๋ค.
Activity / Fragment ์ Lifecycle ์ฒ๋ฆฌ๋ฅผ ๋ฐ๋ก ๊ตฌ์ฑ
Activity ๋ Lifecycle Owner ๊ฐ ๋๋ฉฐ ์ด๋ฅผ ์ฒ๋ฆฌํ๋ ๊ณณ์ด Lifecycle Observer
Lifecycle ๊ด๋ จ ์์ ์ Observer ์์ ์ฒ๋ฆฌํ๋ ๋ฐฉ์. Observer ๋ Owner์ Lifecycle ๋ณํ๋ฅผ ๊ฐ์งํ๋ค.
์ฆ, Lifecycle ๊ด๋ จ ์ฝ๋๋ฅผ ๋ถ๋ฆฌ์ํด์ผ๋ก์ ํธํ๊ฒ ์ฝ๋ ์์ฑ์ ํ ์ ์๊ฒ๋ ์ง์ํ๋ ๊ฒ์ด ๋ชฉ์ ์ธ ์ปดํฌ๋ํธ๋ผ๊ณ ๋ณผ ์ ์๋ค.
Navigation Component ๋ ํ๋ฉด๊ฐ ์ด๋์ ๊ฐํธํ๊ฒ ์์ฑํ๊ฒ ํ๋ JetPack ์ Library ํน์ Tool
ํ๋์ main activity ์ ์ฌ๋ฌ ๊ฐ์ fragment ๋ก ํ๋ฉด์ ์ค๊ณํ๋ ๊ฒ์ ๋ชฉ์ ์ผ๋ก ํจ
ํ๋ฉด์ fragment ๋ก ๊ตฌ์ฑํ ๋ ํ๋ฉด ์ ํ ํ๋ฆ๊ณผ ์ ๋ฌ๋๋ ๋ฐ์ดํฐ๋ฅผ ํ๋์ ๋ณผ ์ ์๊ฒ ๋์ํ ํด์ฃผ๋ ์ปดํฌ๋ํธ.
SOLite ์ถ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
์๋๋ก์ด๋ ์ฑ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด์ฉ์ Room ์ผ๋ก ๊ถ๊ณ
Retrofit ๊ณผ ๋น์ทํ ๋ชฉ์ ์ ๊ฐ์ง๋ค. ์ฐ๋ฆฌ๊ฐ ์ธํฐํ์ด์ค๋ฅผ ์์ฑํ๊ณ ์ด๋
ธํ
์ด์
๊ณผ ๋ฐ์ดํฐ๋ฅผ ์ฃผ๋ฉด, Room ์์ ์์์ SQL ๊ตฌํ์ฒด๋ฅผ ๋ง๋ค์ด ์ค๋ค. DBMS ๋ฅผ ์ํ ์ปดํฌ๋ํธ.
RecyclerView ์์ ํ์ด์ง ์ฒ๋ฆฌ๋ฅผ ์ฝ๊ณ ํจ์จ์ ์ผ๋ก ์์ฑํ๊ฒ ํ๊ธฐ ์ํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
ํ์ด์ง ๋ ๋ฐ์ดํฐ์ ๋ฉ๋ชจ๋ฆฌ ๋ด ์บ์ฑ
์์ฒญ ์ค๋ณต ์ ๊ฑฐ ๊ธฐ๋ฅ์ด ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณต
๋ฐ์ดํฐ์ ๋๊น์ง ์คํฌ๋กคํ ๋ ์ด๋ํฐ๊ฐ ์๋์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์์ฒญ
WorkManager ๋ ํน์ ์์ ์ ์ ์(Defer) ์ฒ๋ฆฌ, ๋น๋๊ธฐ ์ฒ๋ฆฌ๋ฅผ ์ ๊ณต
JobScheduler, FirebaseJobDispatcher, AlarmManager ๋ฑ์ ํตํฉํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
๊ถ์ฅ์ฌํญ์ ์ดํ๋ฆฌ์ผ์ด์ ์ Separation of concerns ์ ์ํด ์์ฑ
๊ทธ๋ก ์ธํ UI ์ ๋ชจ๋ธ์ด ๋ถ๋ฆฌ
AAC ์ ํต์ฌ ์ํคํ ์ฒ ์ด๋ค.
์๋ ์ผ๋ฐ์ ์ผ๋ก ๋ง์ด ์ฌ์ฉํ๊ณ ์๋ ์ํํธ์จ์ด ๋ชจ๋ธ.
์ฃผ๋ก ํ๋ก ํธ์๋ ์ดํ๋ฆฌ์ผ์ด์
์ ๋ค์ด๊ฐ๋ ๋ชจ๋ธ์ด๋ค. ํ๋ก ํธ์๋ ์ดํ๋ฆฌ์ผ์ด์
์ ์ฃผ๋ ๋ชฉ์ ์ ํ๋ฉด
, ์ฆ View
์ด๋ค. AAC ๋ ์ด MVVM ์ ๋ชฉ์ ์ผ๋ก ํ๋ค.
Model + View + ViewModel = MVVM
Model : ์ค์ ๋น์ฆ๋์ค ์ ๋ฌด ๋ก์ง์ด ๊ตฌํ๋๋ ๋ถ๋ถ, ํน์ ๋น์ฆ๋์ค ๋ฐ์ดํฐ๊ฐ ํํ๋๋ ๋ถ๋ถ (๋ฐ์, ๊ด๋ฆฌ)
View : ํ๋ฉด, ํํ Presentation ๋ก์ง์ด๋ผ๊ณ ํํํ๋ค.
ViewModel : Model ๊ณผ View ์ ๋งค๊ฐ ์ญํ ์. Model ์ด ๊ต์ฒด ๋๋๋ผ๋ View ์ํฅ์ด ๋ฏธ์น์ง ์๋๋ค.
์ฆ, ํ๋ฉด์ ๊ตฌ์ฑํ๋ UI, View ์ ๋น์ฆ๋์ค ์
๋ฌด ๋ก์ง์ ์ ๊ณตํ๋ Model ์ ๋ถ๋ฆฌํ๋ค๋ ์๋ฏธ.
ํ๋ฉด ์ถ๋ ฅ์ Activity ํน์ Fragment ๊ฐ์ View
์์ ์ฒ๋ฆฌํ๋ค. ์
๋ฌด ๋ก์ง์ Repository
์ ๋ด๊ณ DB ๊ด๋ จ์ Room
, ์น ๊ด๋ จ์ Retrofit
์ผ๋ก ๊ตฌํํ๋ค. ๊ทธ๋ฆฌ๊ณ View ์ Repository ์ฌ์ด์ ๋งค๊ฐ ์ญํ ์ ViewModel
์ ๋์ด View ๋ ์ด๋ฒคํธ ์
๋ ฅ๋ง ๋ฐ๊ณ , Repository ๋ ์ด๋ฒคํธ ์ฒ๋ฆฌ๋ง ์์
ํ๋ค. ์ฒ๋ฆฌ๋ ๋ฐ์ดํฐ๋ Repository -> ViewMoel
๋ก ์ ๋ฌ๋์ด LiveData
๋ก View ์ ์ ๋ฌ๋๋ค. ๋ํ Lifecycle
๊น์ง Owner ์์ ๋ถ๋ฆฌํ์ฌ ์ ์ดํ๋ค.
Activity / Fragment : View
- UI ๋ฅผ ๊ตฌ์ฑํ๊ณ ์ ์ ์ ์ํธ ์์ฉ. Lifecycle ๋ณ๊ฒฝ์ ๊ฐ์ง
- Lifecycle ๋ณ๊ฒฝ์ด ๋ฐ์ํ๋ฉด ViewModel ์ ์ ๋ฌ
- ViewModel ์์ ์ ๋ฌํ LiveData ์ ๋ด์ฉ์ ํ๋ฉด์ ์ถ๋ ฅ
ViewModel : View ์ Model ์ ๋ถ๋ฆฌ ์ญํ ์
- View ์ Model ์ ๋ถ๋ฆฌ์ํค๊ธฐ ์ํ ๊ฐ๊ต ์ญํ
- View ์ Lifecycle ๋ณ๊ฒฝ์ ๊ฐ์งํ๊ณ ๋ณ๊ฒฝ์ด ๋ฐ์ํ๋ฉด Repository ์ ์คํ
Repository : ๋ฐ์ดํฐ ์ฒ๋ฆฌ, ํํ ๋งํ๋ 'Model'
- ๋ฐ์ดํฐ์ ์ ์ฅ ๋ฐ ํ๋์ด ์ฃผ ๋ชฉ์
- Room : ๋ฐ์ดํฐ ์์ํ
- SQLite ๋งคํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
MVVM ์ ํต์ฌ์ ํ๋ฉด์์ B/L ์ ๋ถ๋ฆฌํด์ ๊ฐ๋ฐํ๋ ๊ฒ
Activity, Fragment ์์ UI ๋ฅผ ์ฒ๋ฆฌํ๊ณ ViewModel ์์ B/L ์ ์ฒ๋ฆฌ
Activity ์์ onSaveInstanceState() ํจ์๋ฅผ ์ด์ฉํด ์์ฑํ๋ Bundle ๋ฐ์ดํฐ ์ ์ฅ๋ ViewModel ๋ก ๋์ฒด๊ฐ ๊ฐ๋ฅ
val lifecycle_version = "2.5.1"
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
class MyViewModel: ViewModel() {
val user: MutableLiveData<User>
get() {
val user = MutableLiveData<User>()
user.postValue(User("Gildong", "hong1"))
return user
}
}
val model = ViewModelProvider(this).get(MyViewModel::class.java)
model.user.observe(this, {
binding.textView.text = "${it.firstName} - ${it.lastName}"
})
Activity ์์ ViewModel ์ด์ฉ
ViewModelProvider ๋ฅผ ์ด์ฉํ ๊ฐ์ฒด ์์ฑ
์ง์ ๊ฐ์ฒด ์์ฑ๋ ๊ฐ๋ฅ
ViewModelProvider ์ ์ด์ฉํด ๊ฐ์ฒด ์์ฑ์ ํด์ผ Activity ์ํ ๋ณํ์ ๋ฐ๋ฅธ ViewModel ์ด ์ฌ์์ฑ๋๋ ๊ฒ์ ๋ง์ ์ ์์ = ์ง์ ๊ฐ์ฒด ์์ฑ ์ ViewModel ์ ์ํ ๋ฐ์ดํฐ ์ ์ฅ ๊ธฐ๋ฅ์ ๋ชป ์
val model: MyViewModel by viewModels()
ViewModelProvider ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ๊ฒ์ ์ข ๋ ๋จ์ํ์ํจ delegator ๊ฐ ์ ๊ณต๋๋ค. ์์ฆ ๊ฑฐ์ ์ด delegator ์ด์ฉํ๋ ์ถ์ธ๋ผ๊ณ ํ๋ค.
์ฝํ๋ฆฐ ํ๋ก๊ทธ๋จ์์ by
๋ค์ด๊ฐ๋ ๊ฒ์ด delegator ๋ฅผ ๋ปํ๋ค.
์ฌ์ฉํ๋ ค๋ฉด dependency ๋ฅผ ์ถ๊ฐํด ์ฃผ์ด์ผ ํ๋ค.
val activity_version = "1.6.1"
implementation("androidx.activity:activity-ktx:$activity_version")
ViewModel Lifecycle
Activity Scope ๋ด์์ Singleton
ViewModel ์ ๊ฐ์ฒด๋ ๊ฒฐ๊ตญ ์กํฐ๋นํฐ์์ ํ๋ํ๋ค. ์กํฐ๋นํฐ๋ ์ํ ๋ณํ(ํ๋ฉด ํ์ ๋ฑ) ๊ฐ ๋ฐ์ํ๋ฉด ์ข ๋ฃ๋๋ฉด์ ๋ชจ๋ ๋ฐ์ดํฐ๊ฐ ๋ ๋ผ๊ฐ์ง๋ง, ViewModelProvider ๋ฅผ ์ฌ์ฉํด ์ป์ ViewModel ๊ฐ์ฒด๋ ์ข ๋ฃ๋์ง ์๋๋ค. ViewModel ๋ด์ ๋ฐ์ดํฐ๊ฐ ์๋ค๋ฉด View ๋ ํด๋น ๋ฐ์ดํฐ๋ฅผ ์ป์ด์ค๊ธฐ๋ง ํ๋ฉด ๋๊ธฐ ๋๋ฌธ์, bundle ์ ๋์ฒดํ๋ ๊ธฐ๋ฒ์ผ๋ก ์ฌ์ฉํ ์ ์๋ ๊ฒ์ด๋ค.
๋ํ ํ๋์ ์กํฐ๋นํฐ์์ ์ฌ๋ฌ ๋ฒ ์์ฑ ํ๋๋ผ๋ Singleton ์ผ๋ก ์ ์ง๊ฐ ๋๋ค.
AndroidViewModel
์ ViewModel ์ ์๋ธ ํด๋์ค
ViewModel() // ๋งค๊ฐ๋ณ์ x
AndroidViewModel(Application application) // ๋งค๊ฐ๋ณ์๋ก context ์ด์ฉ
ViewModel ์ ์์ฑํ ๋ ViewModel()
์ ์์ ๋ฐ์ ์์ฑํ๋ ๊ฒ์ด ์ผ๋ฐ์ ์ด๋ค. ์ด๋ ViewModel
์ ์์ฑ์์๋ ๋งค๊ฐ๋ณ์๊ฐ ์๋ค. ๊ทธ๋ฐ๋ฐ ๊ฐ๋ AndroidViewModel(Application application)
์ ์์ ๋ฐ์ ViewModel ์ ์์ฑํ ๋๊ฐ ์๋ค. ์ด๋ ViewModel ๋ด์์ ์ปจํ
์คํธ ๊ฐ์ฒด, ์ดํ๋ฆฌ์ผ์ด์
๊ฐ์ฒด๋ฅผ ์ด์ฉํ๊ณ ์ ํ๋ ๊ฒฝ์ฐ์ด๋ค.
Fragment ์์ ViewModel ์ด์ฉ
Activity ๋ด์ Fragment ๊ฐ ์ฌ๋ฌ ๊ฐ ์๋ ๊ฒฝ์ฐ Fragment ๊ฐ ViewModel ๊ณต์ ํ์
ViewModel ์ Lifecycle ์ Owner ๋ฅผ ๋ฐ๋ผ๊ฐ๋ค.
ํ๋๊ทธ๋จผํธ ๊ฐ๊ฐ ViewModel ์ ์ค ์๋ ์๊ณ , ํ๋์ ViewModel ์ ๊ณต์ ํ ์๋ ์๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ๋ ViewModel ์ Lifecycle ์ ๋๊ตฌ๋ฅผ Owner ๋ก ์ง์ ํ๋์ง์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ค.
val model = ViewModelProvider(requireActivity()).get(MyFragmentViewModel::class.java)
ViewModelProvider ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ get()
ํจ์๋ก ViewModel ์ ๋ณด๋ฅผ ์ค๋ค.
๊ทธ๋ฆฌ๊ณ ViewModelProvider ์ ๋งค๊ฐ๋ณ์๋ก Lifecycle Owner
๋ฅผ ์ค๋ค. Owner ๋ฅผ ์กํฐ๋นํฐ๋ก ์ง์ ํ๋ฉด ์ฌ๋ฌ ๊ฐ์ ํ๋๊ทธ๋จผํธ์์ ๊ณต์ ํ ์ ์๊ณ , ํ๋๊ทธ๋จผํธ๋ก ์ง์ ํ๋ฉด ํด๋น ํ๋๊ทธ๋จผํธ์ ๋ผ์ดํ์ฌ์ดํด๋ง ๊ฐ์งํ๊ธฐ ๋๋ฌธ์ ๊ณต์ ๋์ง ์๋๋ค.
class MyViewModel: ViewModel() {
fun someData(): String{
return "hello"
}
fun someData2(): MutableLiveData<String> {
val liveData = MutableLiveData<String>()
thread {
SystemClock.sleep(3000)
liveData.postValue("world")
}
return liveData
}
}
ViewModel ์ ๊ฒฐ๊ณผ
ViewModel ์์ String ๋ฑ์ ๊ฒฐ๊ณผ๋ฅผ ๋ฆฌํด ์ํฌ ์๋ ์์ง๋ง LiveData ์ ๋ฆฌํด ์์ผ Observer ๋ก ๊ฒฐ๊ณผ ์ด์ฉ
์ผ๋จ ๋ฆฌํด๊ฐ์ ๋๊ธด ํ์ Observer ๊ฐ ๊ฐ ๋ณ๊ฒฝ์ ๊ฐ์งํ๋ฉด ๋ฐ์ดํฐ๋ฅผ ์ ๋ฌํ๋ค.
val observer = object : Observer<String> {
override fun onChanged(t: String?) {
Log.d("kotdev", "onChanged...$t")
}
}
model.someData2().observe(this, observer)
val liveData = model.someData2()
//.................
liveData.removeObservers(this)
LiveData ์ ๋ณ๊ฒฝ์ ๊ฐ์งํ๋ observer ๋ Observer ๋ฅผ ๊ตฌํํ ํด๋์ค
LiveData ์ ๊ฐ์ด ๋ณ๊ฒฝ๋๋ฉด Observer ์ onChanged() ํจ์๊ฐ ์๋ ํธ์ถ
๋ ์ด์ ๊ฐ์ง๊ฐ ํ์ ์๋ ๊ฒฝ์ฐ ๋ช
์์ ์ผ๋ก removeObservers ํจ์ ํธ์ถ
class MyLiveData : LiveData<String>() {
fun sayHello(name: String) {
postValue("Hello $name")
}
}
val liveData1 = MyLiveData()
liveData1.observe(this) {
Log.d(
"kotdev", "result: $it"
)
}
liveData1.sayHello("kotdev")
LiveData ๋ฅผ ์์ ๋ฐ์ ์์ฑ
ViewModel ์ด์ธ์ ๋ค๋ฅธ ๊ณณ์์ ์ฌ์ฉ ๊ฐ๋ฅ
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1")
implementation("androidx.activity:activity-ktx:$1.6.1")
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/holo_blue_light"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/resultView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:textSize="25dp"
android:textStyle="bold" />
<Button
android:id="@+id/button"
style="@style/Widget.MaterialComponents.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="call sum" />
</LinearLayout>
package com.kotdev99.android.c100
class MyViewModel : ViewModel() {
var sum = 0
fun calSum(): MutableLiveData<String> {
val liveData = MutableLiveData<String>()
thread {
for (i in 1..10) {
sum += i
liveData.postValue(sum.toString())
SystemClock.sleep(100)
}
}
return liveData
}
}
package com.kotdev99.android.c100
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val model: MyViewModel by viewModels() // ViewModelProvider ๋ฅผ ์ด์ฉํ ๊ฒ๊ณผ ๋์ผ
binding.button.setOnClickListener {
model.calSum().observe(this) {
binding.resultView.text = it
}
}
}
}