일반적으로 LiveData에 어떤 데이터가 설정되면 이 데이터를 관찰하고 있는 Observer들에게 업데이트 이벤트를 전달합니다. 이전에 설정된 데이터와 동일한 값을 다시 set해도 Observer들에게 매번 이벤트를 전달하는데요. 경우에 따라서 불필요한 작업을 여러번 수행하게 만들 수 있습니다.
distinctUntilChanged()
는 이런 부분을 해결하기 위해 도입된 라이브러리입니다. 이 메소드는 중복은 무시하고, 실제 데이터의 변경이 있을 때만 업데이트 이벤트를 전달하는 LiveData를 생성합니다. 이렇게 생성된 LiveData를 이용하면, 중복된 값이 설정되도 업데이트 이벤트를 발생하지 않아 Observer가 업데이트 이벤트를 수신하지 않게 됩니다.
/**
* Creates a new {@link LiveData} object does not emit a value until the source LiveData value
* has been changed. The value is considered changed if {@code equals()} yields {@code false}.
*
* @param source the input {@link LiveData}
* @param <X> the generic type parameter of {@code source}
* @return a new {@link LiveData} of type {@code X}
*/
@MainThread
@NonNull
public static <X> LiveData<X> distinctUntilChanged(@NonNull LiveData<X> source)
androidx.lifecycle:lifecycle-*:2.1.0 버전에서
Transformations.distinctUntilChanged()
가 추가되었습니다.
1. Example : distinctUntilChanged()으로 LiveData 객체 생성
distinctUntilChanged()
으로 LiveData를 생성하고, 동일한 값을 설정했을 때 Observer에게 업데이트 이벤트가 전달 안되는지 확인하려고 합니다.
샘플 앱은 버튼을 클릭하면 EditText에 입력된 숫자를 LiveData에 set하고, Observer로 업데이트 이벤트가 전달되면 그 데이터를 TextView에 보여줍니다.
다음과 같이 샘플 앱을 구현하였습니다. 전체 코드는 GitHub - LiveDataDistinctUntilChangedSample에 올려두었으니 자세한 내용은 여기서 확인해주세요.
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
val viewModel = ViewModelProviders.of(this).get(MainActivityViewModel::class.java)
binding.lifecycleOwner = this
binding.viewModel = viewModel
binding.button.setOnClickListener {
val input = binding.editText.text.toString().toInt()
Log.d("MainActivity", "Button clicked, input value: $input")
viewModel.setValue(input)
}
viewModel.value.observe(this) {
Log.d("MainActivity", "LiveData updated: $it")
}
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable name="viewModel"
type="com.example.distinctuntilchanged.MainActivityViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.value.toString()}"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/editText"
android:layout_width="240dp"
android:layout_height="wrap_content"
android:inputType="number"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/textView" />
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="CLICK"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/editText" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
MainActivityViewModel.kt
class MainActivityViewModel : ViewModel() {
private val _value: MutableLiveData<Int> = MutableLiveData()
val value: LiveData<Int> = Transformations.distinctUntilChanged(_value)
fun setValue(value: Int) {
_value.postValue(value)
}
}
동작 확인
App을 실행해서 다음과 같이 동일한 데이터를 여러번 LiveData에 set하였습니다.
- EditText에
123
입력 후 버튼 3회 클릭 - EditText에
1234
입력 후 버튼 3회 클릭
결과를 보면, LiveData에 동일한 value가 set될 때는 업데이트 이벤트가 발생하지 않았습니다.
01-09 10:56:38.746 6440 6440 D MainActivity: Button clicked, input value: 123
01-09 10:56:38.748 6440 6440 D MainActivity: LiveData updated: 123
01-09 10:56:41.329 6440 6440 D MainActivity: Button clicked, input value: 123
01-09 10:56:43.234 6440 6440 D MainActivity: Button clicked, input value: 123
01-09 10:56:47.009 6440 6440 D MainActivity: Button clicked, input value: 1234
01-09 10:56:47.010 6440 6440 D MainActivity: LiveData updated: 1234
01-09 10:56:48.053 6440 6440 D MainActivity: Button clicked, input value: 1234
01-09 10:56:48.745 6440 6440 D MainActivity: Button clicked, input value: 1234
2. Example : distinctUntilChanged()를 사용하지 않고, LiveData 객체 생성
다음과 같이 distinctUntilChanged()
를 사용하지 않고, LiveData를 생성하였습니다. 다른 코드들은 위와 동일합니다.
class MainActivityViewModel : ViewModel() {
private val _value: MutableLiveData<Int> = MutableLiveData()
val value: LiveData<Int> = _value
fun setValue(value: Int) {
_value.postValue(value)
}
}
App을 실행해서 다음과 같이 동일한 데이터를 여러번 LiveData에 set하였습니다.
- EditText에
123
입력 후 버튼 3회 클릭 - EditText에
1234
입력 후 버튼 3회 클릭
결과를 보면, 동일한 데이터가 LiveData에 설정될 때 업데이트 이벤트가 발생하여 Observer에게 전달되었습니다.
01-09 11:09:26.190 6906 6906 D MainActivity: Button clicked, input value: 123
01-09 11:09:26.191 6906 6906 D MainActivity: LiveData updated: 123
01-09 11:09:26.778 6906 6906 D MainActivity: Button clicked, input value: 123
01-09 11:09:26.779 6906 6906 D MainActivity: LiveData updated: 123
01-09 11:09:27.378 6906 6906 D MainActivity: Button clicked, input value: 123
01-09 11:09:27.380 6906 6906 D MainActivity: LiveData updated: 123
01-09 11:09:28.930 6906 6906 D MainActivity: Button clicked, input value: 1234
01-09 11:09:28.932 6906 6906 D MainActivity: LiveData updated: 1234
01-09 11:09:29.474 6906 6906 D MainActivity: Button clicked, input value: 1234
01-09 11:09:29.476 6906 6906 D MainActivity: LiveData updated: 1234
01-09 11:09:30.002 6906 6906 D MainActivity: Button clicked, input value: 1234
01-09 11:09:30.003 6906 6906 D MainActivity: LiveData updated: 1234
3. Transformations.distinctUntilChanged()의 라이브러리 구현 내용
AOSP - Introduce Transformations.distinctUntilChanged에 이 메소드의 구현 내용을 확인할 수 있습니다.
코드를 보시면 onChanged
가 발생했을 때, 이전 데이터와 다를 때만 setValue()
를 하여 업데이트를 하도록 구현되어있습니다.
References
Related Posts
- Android 14 - 사진/동영상 파일, 일부 접근 권한 소개
- Android - adb push, pull로 파일 복사, 다운로드
- Android 14 - 암시적 인텐트 변경사항 및 문제 해결
- Jetpack Compose - Row와 Column
- Android 13, AOSP 오픈소스 다운로드 및 빌드
- Android 13 - 세분화된 미디어 파일 권한
- Android 13에서 Notification 권한 요청, 알림 띄우기
- Android 13에서 'Access blocked: ComponentInfo' 에러 해결
- 에러 해결: android gradle plugin requires java 11 to run. you are currently using java 1.8.
- 안드로이드 - 코루틴과 Retrofit으로 비동기 통신 예제
- 안드로이드 - 코루틴으로 URL 이미지 불러오기
- Android - 진동, Vibrator, VibrationEffect 예제
- Some problems were found with the configuration of task 에러 수정
- Query method parameters should either be a type that can be converted into a database column or a List
- 우분투에서 Android 12 오픈소스 다운로드 및 빌드
- Android - ViewModel을 생성하는 방법
- Android - Transformations.map(), switchMap() 차이점
- Android - Transformations.distinctUntilChanged() 소개
- Android - TabLayout 구현 방법 (+ ViewPager2)
- Android - 휴대폰 전화번호 가져오는 방법
- Android 12 - Splash Screens 알아보기
- Android 12 - Incremental Install (Play as you Download) 소개
- Android - adb 명령어로 bugreport 로그 파일 추출
- Android - adb 명령어로 App 데이터 삭제
- Android - adb 명령어로 앱 비활성화, 활성화
- Android - adb 명령어로 특정 패키지의 PID 찾기
- Android - adb 명령어로 퍼미션 Grant 또는 Revoke
- Android - adb 명령어로 apk 설치, 삭제
- Android - adb 명령어로 특정 패키지의 프로세스 종료
- Android - adb 명령어로 screen capture 저장
- Android - adb 명령어로 System 앱 삭제, 설치
- Android - adb 명령어로 settings value 확인, 변경
- Android 12 - IntentFilter의 exported 명시적 선언
- Android - adb 명령어로 공장초기화(Factory reset)
- Android - adb logcat 명령어로 로그 출력