Android Developer의 한글 공식문서입니다.
https://developer.android.com/topic/libraries/architecture/livedata?hl=ko#kotlin
내용은 다음과 같습니다.
LiveData를 사용하면 다음의 이점이 있습니다.
UI와 데이터 상태의 일치 보장
LiveData는 관찰자 패턴을 따릅니다. LiveData는 기본 데이터가 변경될 때 Observer객체에 알립니다. 코드를 통합하여 이러한 observer객체에 UI를 업데이트 할 수 있습니다. 이렇게 하면 앱 데이터가 변경될 때마다 관찰자가 대신 UI를 업데이트 하므로 개발자가 업데이트할 필요가 없습니다.
메모리 누수 없음
관찰자는 Lifecycle객체에 결합되어 있으며 연결된 수명 주기가 끝나면 자동으로 삭제됩니다.
중지된 활동으로 인한 비정상 종료 없음
활동이 백 스택에 있을 때를 비롯하여 관찰자의 수명 주기가 비활성 상태에 있으면 관찰자는 어떤 LiveData 이벤트도 받지 않습니다.
수명 주기를 더 이상 수동으로 처리하지 않음
UI구성요소는 관련 데이터를 관찰하기만 할 뿐 관찰을 중지하거나 다시 시작하지 않습니다. LiveData는 관찰하는 동안 관련 수명 주기 상태의 변경을 인식하므로 이 모든 것을 자동으로 관리합니다.
적절한 구성 변경
기기 회전과 같은 구성 변경으로 인해 활동 또는 프래그먼트가 다시 생성되면 사용 가능한 최신 데이터를 즉시 받게 됩니다.
리소스 공유
앱에서 시스템 서비스를 공유할 수 있도록 싱글톤 패턴을 사용하는 LiveDta객체를 확장하여 시스템 서비스를 래핑할 수 있습니다. LiveData객체가 시스템 서비스에 한 번 연결되면 리소스가 필요한 관찰자가 LiveData객체를 볼 수 있습니다.
아래는 LiveData를 사용한 예제코드 입니다.
자세하게 말하면 이번에는 ViewBinding을 사용하고 LiveData관련 2가지 케이스를 가지고 비교해 보겠습니다.
plugins {
id 'com.android.application'
id 'kotlin-android'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.3"
defaultConfig {
applicationId "com.example.livedata_kotlin"
minSdkVersion 21
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'
}
//ViewBinding사용!
buildFeatures{
viewBinding = true;
}
}
dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.6.0'
implementation 'androidx.appcompat:appcompat:1.3.1'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.1'
testImplementation 'junit:junit:4.+'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
//ViewModel 및 LiveData사용!
def lifecycle_version = "2.0.0"
implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
}
//MainActivity.kt
class MainActivity : AppCompatActivity() {
//binding
private lateinit var binding: ActivityMainBinding
private lateinit var mModel: RandomNumberViewModel
private lateinit var mModel2: TimerViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
//TODO: 예제1
//Get the view model
// (참고)Lifecycle -extension 라이브러리가 2.2.0으로 버전업하면서 ViewModelProviders는 Deprecate되었습니다
// mModel = ViewModelProviders.of(this).get(RandomNumberViewModel::class.java) //구버전
mModel = ViewModelProvider(this).get(RandomNumberViewModel::class.java) //신버전
//Create the observer which updates the ui
val randomNumberObserver = Observer<Int> { newNumber ->
//Update the ui wuth current data
binding.mTextview.text = "Current Number : $newNumber"
}
//Observe the live data, passing in this activity as the life cycle owner and the observer
mModel.currentRandomNUmber.observe(this, randomNumberObserver)
//Button click listener
binding.mButton.setOnClickListener {
//Change the data
mModel.currentRandomNUmber.value = Random().nextInt(50)
}
//TODO: 예제2
//Get the view model
mModel2 = ViewModelProvider(this).get(TimerViewModel::class.java)
//Observe case1
mModel2.seconds().observe(this, Observer {
binding.mTextview2.text = it.toString()
})
//Observe case2
mModel2.finished.observe(this, Observer {
if(it){
Toast.makeText(this, "Finished!", Toast.LENGTH_SHORT).show()
}
})
binding.mTimeStart.setOnClickListener {
if(binding.mInputText2.text.isEmpty() || binding.mInputText2.text.length>10){
Toast.makeText(this, "Invaild Number!", Toast.LENGTH_SHORT).show()
}else{
mModel2.timerValue.value = binding.mInputText2.text.toString().toLong()
mModel2.startTimer()
}
}
binding.mTimeFinish.setOnClickListener {
binding.mTextview2.text = "0"
mModel2.stopTimer()
}
}
}
//RandomNumberViewModel.kt
class RandomNumberViewModel : ViewModel() {
//Create a LiveData with a random number
val currentRandomNUmber: MutableLiveData<Int> by lazy {
MutableLiveData<Int>()
}
}
//TimerViewModel.kt
class TimerViewModel : ViewModel() {
private lateinit var timer: CountDownTimer
var timerValue = MutableLiveData<Long>()
private val _seconds = MutableLiveData<Int>() //Observe
var finished = MutableLiveData<Boolean>() //Observe
fun seconds(): LiveData<Int>{
return _seconds
}
//이렇게 구문을 사용하는 이유는
//실제 _seconds를 직접접근하는것이 아니라 읽기만 가능한 val의 seconds를 접근하여 원본이 수정되지 않도록 하기 위해서!
val seconds: LiveData<Int>
get() = _seconds
fun startTimer(){
timer = object: CountDownTimer(timerValue.value!!.toLong(), 1000){
override fun onTick(millisUntilFinished: Long) {
val timeLeft = millisUntilFinished/1000
_seconds.value = timeLeft.toInt()
}
override fun onFinish() {
finished.value = true
}
}.start()
}
fun stopTimer(){
timer.cancel()
}
}
<?xml version="1.0" encoding="utf-8"?>
<!-- activity_main.xml -->
<LinearLayout 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"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="@+id/m_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#F0F0F0"
android:text="랜덤 숫자 생성" />
<TextView
android:id="@+id/m_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="#F0F0F0"
android:padding="10dp"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:background="#FF0000" />
<EditText
android:id="@+id/m_inputText2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#F0F0F0"
android:padding="10dp"
android:text="10000" />
<TextView
android:id="@+id/m_textview2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#00F0F0"
android:padding="10dp"
android:text="시간" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/m_time_start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:background="#F0F0F0"
android:padding="10dp"
android:text="시작" />
<TextView
android:id="@+id/m_time_finish"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#F0F0F0"
android:padding="10dp"
android:text="종료" />
</LinearLayout>
</LinearLayout>
'🖥 Programming > 📱 Android (Kotlin)' 카테고리의 다른 글
[Android] This version of the Android Support plugin for IntelliJ IDEA (or Android Studio) cannot open this project, please retry with version 4.2 or newer. (0) | 2021.10.30 |
---|---|
[Android][kotlin] MVVM 패턴공부 (0) | 2021.10.30 |
[Android][kotlin] DataBinding & LiveData 같이 사용하기 (0) | 2021.10.28 |
[Andorid][kotlin] Android JetPack 구성도 (0) | 2021.10.27 |
[Android][kotlin] ViewBinding (뷰바인딩) (0) | 2021.10.25 |
[Android][kotlin] Execution failed for task ':app:dataBindingMergeDependencyArtifactsDebug'. 오류 해결 (0) | 2021.10.22 |
[Android] RecyclerView (리사이클러뷰) 알아보기 (0) | 2021.10.22 |
[Android][kotlin] No type arguments expected for class Call 에러 (0) | 2021.10.21 |