#1 ViewModel ์์ LiveData๊ฐ ์๋ ์ํ ์ฑ
[Android] ViewModel - View์ ๊ฐ์ฒด(ViewModel) ์ ๋ฌ
#1 ๊ฐ์ #1-1 Data Binding๊ณผ ViewModel [Android] Data Binding - View์ ๊ฐ์ฒด ์ ๋ฌ #1 ๊ฐ์ฒด ์ ๋ฌ์ ํ์์ฑ #1-1 ์ด์ ๊ธ Data Binding - ๊ธฐ์ด #1 ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ์ฌ์ฉ ์ #1-1 ์์ ์ฑ ์๊ณผ ๊ฐ์ ๊ฐ๋จํ ์ฑ์ด ์๋ค. Button์
kenel.tistory.com
์ ๊ฒ์๊ธ์ ์์ฑ๋ ์ฑ์ ํ ๋๋ก ์ฝ๋๋ฅผ ์์ ํ๋ค.
#2 ๋ช ์์ LiveData.observe()
[Android] LiveData - ๊ธฐ์ด
#1 ๊ฐ์ #1-1 LiveData LiveData ๊ฐ์ | Android ๊ฐ๋ฐ์ | Android Developers LiveData๋ฅผ ์ฌ์ฉํ์ฌ ์๋ช ์ฃผ๊ธฐ๋ฅผ ์ธ์ํ๋ ๋ฐฉ์์ผ๋ก ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค. developer.android.com LiveData. ๋ฌธ์์ ์ธ ์ ์๋ Data์ ๋ณ๊ฒฝ์ ๊ด
kenel.tistory.com
์ด LiveData๊ฐ '์ด์์์ ์' ์๋ ์ด์ ๋ LiveData.observe() ๋๋ถ์ด๋ค. LiveData.observe()๋ ์ฒซ๋ฒ์งธ ์ธ์๋ก MainActivity, Fragment, Service์ ์๋ช
์ฃผ๊ธฐ๋ฅผ ๋ฐ๊ณ , ๋๋ฒ์งธ ์ธ์๋ก๋ LiveData ๊ฐ์ ๋ณ๊ฒฝ ์ ์ํํ ์์
์ ๋ฐ๋๋ค. ๋ฐ๋ผ์, ํ๋ก๊ทธ๋๋จธ๋ LiveData์ ๊ฐฏ์๋งํผ LiveData.observe()๋ฅผ ๋ช
์์ ์ผ๋ก ์ ์ํด์ฃผ์ด์ผ ํ๋ค. ๋ฒ๊ฑฐ๋กญ๋ค. ์ด observe() ์ ์ ๊ณผ์ ์ด ์์์ ์ผ๋ก ์ํ๋๋ค๋ฉด ํธํ ํ
๋ฐ ๋ง์ด๋ค.
#3 ์์์ LiveData.observe()
#3-1 ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ
์ํคํ ์ฒ ๊ตฌ์ฑ์์์ ๋ ์ด์์ ๋ทฐ ๊ฒฐํฉ | Android ๊ฐ๋ฐ์ | Android Developers
์ํคํ ์ฒ ๊ตฌ์ฑ์์์ ๋ ์ด์์ ๋ทฐ ๊ฒฐํฉ ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ์. AndroidX ๋ผ์ด๋ธ๋ฌ๋ฆฌ์๋ ์ฑ๋ฅ์ด ๋ฐ์ด๋๊ณ ํ ์คํธ์ ์ ์ง๊ด๋ฆฌ๊ฐ ์ฌ์ด
developer.android.com
๋คํํ, ์๋๋ก์ด๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ LiveData.observe()์ ์์์ ์ ์๋ฅผ ์ง์ํ๋ค. ๋ค์ ์ฝ๋๋ฅผ ๋ณด์.
#3-2 ๊ธฐ์กด์ ๋ช ์์ LiveData.observe() ์ฝ๋
...
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MainActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
binding.myViewModel = viewModel
viewModel.getCurrentCount().observe(this, Observer {
binding.countText.text = it.toString()
})
}
}
#1์ ์๋ ์ํ ์ฑ์ ์ฝ๋๋ค. ์๋๋ก์ด๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ํจ๊ป, LiveData.observe()๋ก '๋ช
์์ ๊ด์ฐฐ'์ ํ๊ณ ์๋ค.
#3-3 ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๊ฐ์ฒด๋ ViewDataBinding ํด๋์ค๋ค
package androidx.databinding;
...
/**
* Utility class to create {@link ViewDataBinding} from layouts.
*/
public class DataBindingUtil {
...
}
DataBindingUtil์ ์์ค ์ฝ๋ ์๋จ์๋ Utility class to create ViewDataBinding from layouts๋ผ๊ณ ์ ํ์๋ค. ์ฆ #3-2์ DataBindingUtil.setContentView(this, R.layout.activity_main)๊ฐ ViewDataBinding ํด๋์ค์ ๊ฐ์ฒด๋ฅผ ๋ง๋ค๊ธฐ ์ํ ์ฝ๋์๋ค๋ ์๋ฆฌ๋ค. ์ด๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์ ๊ณตํ๋ ๊ฐ๋ ฅํ ์์์ ์ง์๋ค์ ๊ตฌ์ฌ์ ์ด ๋ฐ๋ก ViewDataBinding ํด๋์ค๋ผ๋ ์ด์ผ๊ธฐ๋ ๋๋ค. ๊ทธ๋ ๋ค๋ฉด, ์ด์ ViewDataBinding ํด๋์ค์ ์์ค ์ฝ๋๋ฅผ ๋ณด๊ฒ ๋ค.
#3-4 ViewDataBinding ํด๋์ค ์ดํด๋ณด๊ธฐ
package androidx.databinding;
...
public abstract class ViewDataBinding extends BaseObservable implements ViewBinding {
...
/**
* Method object extracted out to attach a listener to a bound LiveData object.
*/
private static final CreateWeakListener CREATE_LIVE_DATA_LISTENER = new CreateWeakListener() {
@Override
public WeakListener create(
ViewDataBinding viewDataBinding,
int localFieldId,
ReferenceQueue<ViewDataBinding> referenceQueue
) {
return new LiveDataListener(viewDataBinding, localFieldId, referenceQueue)
.getListener();
}
};
...
/**
* Track the LifecycleOwner set in {@link #setLifecycleOwner(LifecycleOwner)}. Set as
* Object so that the class can be compiled without requiring the LifecycleOwner dependency.
*/
private LifecycleOwner mLifecycleOwner;
...
/**
* Sets the {@link LifecycleOwner} that should be used for observing changes of
* LiveData in this binding. If a {@link LiveData} is in one of the binding expressions
* and no LifecycleOwner is set, the LiveData will not be observed and updates to it
* will not be propagated to the UI.
*
* When using Data Binding with Fragments, make sure to use Fragment.getViewLifecycleOwner().
* Using the Fragment as the LifecycleOwner might cause memory leaks since the Fragment's
* Lifecycle outlives the view Lifecycle.
* When using Data Binding with Activities, you can use the Activity as the LifecycleOwner.
*
* @param lifecycleOwner The LifecycleOwner that should be used for observing changes of
* LiveData in this binding.
*/
@MainThread
public void setLifecycleOwner(@Nullable LifecycleOwner lifecycleOwner) {
if (lifecycleOwner instanceof Fragment) {
Log.w("DataBinding", "Setting the fragment as the LifecycleOwner might cause"
+ " memory leaks because views lives shorter than the Fragment. Consider"
+ " using Fragment's view lifecycle");
}
if (mLifecycleOwner == lifecycleOwner) {
return;
}
if (mLifecycleOwner != null) {
mLifecycleOwner.getLifecycle().removeObserver(mOnStartListener);
}
mLifecycleOwner = lifecycleOwner;
if (lifecycleOwner != null) {
if (mOnStartListener == null) {
mOnStartListener = new OnStartListener(this);
}
lifecycleOwner.getLifecycle().addObserver(mOnStartListener);
}
for (WeakListener<?> weakListener : mLocalFieldObservers) {
if (weakListener != null) {
weakListener.setLifecycleOwner(lifecycleOwner);
}
}
}
...
private static class LiveDataListener implements Observer,
ObservableReference<LiveData<?>> {
final WeakListener<LiveData<?>> mListener;
// keep this weak because listeners might leak, we don't want to leak the owner
// see: b/176886060
@Nullable
WeakReference<LifecycleOwner> mLifecycleOwnerRef = null;
public LiveDataListener(
ViewDataBinding binder,
int localFieldId,
ReferenceQueue<ViewDataBinding> referenceQueue
) {
mListener = new WeakListener(binder, localFieldId, this, referenceQueue);
}
...
@Override
public void setLifecycleOwner(@Nullable LifecycleOwner lifecycleOwner) {
LifecycleOwner previousOwner = getLifecycleOwner();
LifecycleOwner newOwner = lifecycleOwner;
LiveData<?> liveData = mListener.getTarget();
if (liveData != null) {
if (previousOwner != null) {
liveData.removeObserver(this);
}
if (newOwner != null) {
liveData.observe(newOwner, this);
}
}
if (newOwner != null) {
mLifecycleOwnerRef = new WeakReference<LifecycleOwner>(newOwner);
}
}
@Override
public WeakListener<LiveData<?>> getListener() {
return mListener;
}
@Override
public void addListener(LiveData<?> target) {
LifecycleOwner lifecycleOwner = getLifecycleOwner();
if (lifecycleOwner != null) {
target.observe(lifecycleOwner, this);
}
}
...
@Override
public void onChanged(@Nullable Object o) {
ViewDataBinding binder = mListener.getBinder();
if (binder != null) {
binder.handleFieldChange(mListener.mLocalFieldId, mListener.getTarget(), 0);
}
}
}
...
}
setLifecycleOwner์ ์ฃผ์์ ๋ณด๋ฉด, public ๋ฉ์๋์ธ setLifecycleOwner()๋ก ViewDataBinding ๊ฐ์ฒด์ LifecycleOwner๋ฅผ ๋ฑ๋กํด์ฃผ์ด์ผ ๋น๋ก์ LiveData์ '๊ด์ฐฐ(observe)'์ด ์์๋๋ค๊ณ ํ๋ค. ์ฆ LiveData.observe()๊ฐ ์์์ ์ผ๋ก ์ํ๋๋ค๋ ์๊ธฐ๋ค. ๊ทธ ์ ์ฒด์ ์ธ ํ๋ฆ์ ๋ค์๊ณผ ๊ฐ๋ค.
#3-5 ViewDataBinding ํด๋์ค์์ LiveData.observe()๊ฐ ์์์ ์ผ๋ก ์ค์ ๋๋ ํ๋ฆ
target.observe(lifecycleOwner, this)์์ this๋ ๋จ์ผ ๋ฉ์๋ ์ธํฐํ์ด์ค์ธ Observer์ ๊ตฌํ ํด๋์ค๋ค (์ด ๋งํฌ์ #2-3 ์ฐธ์กฐ). ๊ทธ ๊ตฌํ ํด๋์ค๋ ๋ง ๊ทธ๋๋ก this(LiveDataListener)๋ค. LiveDataListener์ ํด๋์ค ์ ์ ๋ถ๋ถ์ ๋ณด๋ฉด Observer๋ฅผ implementํ๊ณ ์์ผ๋ฏ๋ก, LiveDataListener๋ฅผ ๊ทธ๋๋ก ๋ฃ์ด๋ ๋๋ ๊ฒ์ด๋ค. ๋ฐ๋ผ์ LiveDataListener๋ Observer์ ๋จ์ผ ๋ฉ์๋์ธ onChanged()๋ ๊ตฌํํ๊ณ ์๊ณ , LiveData๊ฐ ๋ณ๊ฒฝ๋ ๋๋ง๋ค ํด๋น onChanged()์ ๋ด์ฉ์ด ์คํ๋๋ค.
#4 ์์์ LiveData.observe() ์ฌ์ฉํ๊ธฐ
#4-1 MainActivity.kt์์ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๊ฐ์ฒด์ ์๋ช ์ฃผ๊ธฐ ์ค์ ํ๊ธฐ
...
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
private lateinit var viewModel: MainActivityViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
viewModel = ViewModelProvider(this).get(MainActivityViewModel::class.java)
binding.myViewModel = viewModel
binding.lifecycleOwner = this
/*
viewModel.getCurrentCount().observe(this, Observer {
binding.countText.text = it.toString()
})
*/
}
}
ViewDataBinding ๊ฐ์ฒด์ธ 'binding'์ LifecycleOwner๋ฅผ ๋ฑ๋กํด์ฃผ๋ฉด #3์์ ์๊ฐํ ๊ณผ์ ์ด ์์์ ์งํ๋๋ค. ๋ฐ๋ผ์, ๋ช
์์ ์ผ๋ก ์์ฑํ๋ LiveData.observe() ๊ตฌ๋ฌธ์ ์ ๊ฑฐํ๋ค.
#4-2 activity_main.kt ์์
<?xml version="1.0" encoding="utf-8"?>
<layout ...>
<data>
<variable
name="myViewModel"
type="com.example.implicitobservation.MainActivityViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout ...>
<Button ... />
<TextView
...
android:text="@{(myViewModel.getCurrentCount().toString())}"
... />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
๊ฐ๋จํ ์์ .
#5 ์์ฝ
(์๋๋ก์ด๋ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ ๊ฐ์ฒด) + (์๋ช
์ฃผ๊ธฐ) = (์์์ ์๋ํ๋ LiveData)
#6 ์์ฑ๋ ์ฑ
android-practice/live-data/ImplicitObservation at master · Kanmanemone/android-practice
Contribute to Kanmanemone/android-practice development by creating an account on GitHub.
github.com
'๊นจ์ ๊ฐ๋ ๐ > Android' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[Android] MVVM ๊ตฌ์กฐ ํ๋์ ๋ณด๊ธฐ (0) | 2024.01.22 |
---|---|
[Android] LiveData - ์๋ฐฉํฅ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ์ 3๊ฐ์ง ๋ฐฉ๋ฒ (0) | 2024.01.20 |
[Android] ViewModel - View์ ๊ฐ์ฒด(ViewModel) ์ ๋ฌ (0) | 2024.01.18 |
[Android] LiveData - ๊ธฐ์ด (0) | 2024.01.16 |
[Android] ViewModel - ๋ทฐ ๋ชจ๋ธ์ ์ธ์(Argument) ์ ๋ฌ (ViewModelFactory) (0) | 2024.01.15 |