[Android] Jetpack - LiveData

[Android] Jetpack - LiveData

LiveData๋ฅผ ํ”„๋กœ์ ํŠธ์— importํ•˜๋Š” ๋ฐฉ๋ฒ•์€ ํ”„๋กœ์ ํŠธ์— ์ปดํฌ๋„ŒํŠธ ์ถ”๊ฐ€ํ•˜๊ธฐ ์ฐธ๊ณ 

LiveData

  • Observable Data Holder Class
  • Lifecycle-aware
    • Activity, Fragment, Service ๊ฐ™์€ ์•ฑ ์ปดํฌ๋„ŒํŠธ์˜ Lifecycle๊ณผ ์—ฐ๊ด€
    • ํ™œ์„ฑํ™”(active) ์ƒํƒœ(STARTED ๋˜๋Š” RESUMED)์ธ Observer์—๊ฒŒ๋งŒ ์—…๋ฐ์ดํŠธ๋ฅผ ์•Œ๋ฆผ
  • LifecycleOwner์˜ Lifecycle์ด DESTROYED ์ƒํƒœ๊ฐ€ ๋˜๋ฉด Observer๋ฅผ ์ œ๊ฑฐ
    • leak์— ๋Œ€ํ•œ ๊ฑฑ์ •์—†์ด ์•ˆ์ „ํ•˜๊ฒŒ LiveData๋ฅผ observe ๊ฐ€๋Šฅ
    • Activity์™€ Fragment์—์„œ ํŠนํžˆ ์œ ์šฉ

LiveData ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ํ•œ ๋” ๋งŽ์€ ์ •๋ณด๋Š” Work with LiveData objects

LiveData ์‚ฌ์šฉ์˜ ์žฅ์ 

1. UI์™€ ๋ฐ์ดํ„ฐ ์ƒํƒœ ์ผ์น˜ ๋ณด์žฅ

2. ๋ฉ”๋ชจ๋ฆฌ ๋ฆญ X

์—ฐ๊ฒฐ๋œ Lifecycle์ด destroyed ์ƒํƒœ๊ฐ€ ๋˜๋ฉด ์•Œ์•„์„œ ์ œ๊ฑฐ

3. ์ค‘์ง€๋œ ์•กํ‹ฐ๋น„ํ‹ฐ๋กœ ์ธํ•œ ํฌ๋ž˜์‹œ X

๋น„ํ™œ์„ฑํ™” ์ƒํƒœ(์˜ˆ, ๋ฐฑ์Šคํƒ์— ์žˆ๋Š” Activity)์ด๋ฉด ์–ด๋–ค LiveData ์ด๋ฒคํŠธ๋„ ๋ฐ›์ง€ ์•Š์Œ

4. ์ง์ ‘ Lifecycle์„ ๋‹ค๋ฃฐ ํ•„์š” X

5. ํ•ญ์ƒ ์ตœ์‹ ์˜ ๋ฐ์ดํ„ฐ

Lifecycle์ด ๋น„ํ™œ์„ฑํ™” -> ํ™œ์„ฑํ™” ์ƒํƒœ๋กœ ๋Œ์•„์˜ค๋ฉด ๊ฐ€์žฅ ์ตœ์‹ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์Œ ์˜ˆ) Activity๊ฐ€ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์— ์žˆ๋‹ค๊ฐ€ foreground๋กœ ๋Œ์•„์˜จ ์งํ›„ ์ตœ์‹ ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋ฐ›์Œ

6. ์ ์ ˆํ•œ configuration changes ์ฒ˜๋ฆฌ ๊ฐ€๋Šฅ

ํ™”๋ฉด ํšŒ์ „๊ณผ ๊ฐ™์€ configuration change๋กœ ์ธํ•ด Activity๋‚˜ Fragment๊ฐ€ ์žฌ์ƒ์„ฑ๋œ ๊ฒฝ์šฐ, ๊ฐ€์žฅ ์ตœ์‹ ์˜ ์œ ํšจํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ์ฆ‰์‹œ ๋ฐ›์Œ

7. ์ž์› ๊ณต์œ 

LiveData๋ฅผ ํ™•์žฅํ•˜์—ฌ ์‹œ์Šคํ…œ ์„œ๋น„์Šค๋ฅผ ๊ฐ์‹ธ๋Š” ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“ค์–ด์„œ ์•ฑ์—์„œ ๊ณต์œ  ๊ฐ€๋Šฅ ์‹œ์Šคํ…œ ์„œ๋น„์Šค์™€ LiveData๋ฅผ ํ•œ ๋ฒˆ ์—ฐ๊ฒฐํ•ด๋†“์œผ๋ฉด, ์‹œ์Šคํ…œ ์„œ๋น„์Šค๊ฐ€ ํ•„์š”ํ•œ Observer๋Š” LiveData๋งŒ ๋ฐ”๋ผ๋ณด๋ฉด ๋จ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ LiveData ํ™•์žฅ ํŒŒํŠธ ์ฐธ๊ณ 

LiveData ์‚ฌ์šฉํ•˜๊ธฐ

  1. ํŠน์ • ํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด๋Š” LiveData ์ธ์Šคํ„ด์Šค ์ƒ์„ฑ (๋ณดํ†ต ViewModel ์•ˆ์—์„œ)
  2. Observer ์ƒ์„ฑ ๋ฐ onChanged() ๋ฉ”์„œ๋“œ ์ •์˜ (๋ณดํ†ต Activity/Fragment ๊ฐ™์€ UI ์ปจํŠธ๋กค๋Ÿฌ ์•ˆ์—์„œ)
  3. observe() ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด Observer์™€ LiveData ์—ฐ๊ฒฐ (๋ณดํ†ต Activity/Fragment ๊ฐ™์€ UI ์ปจํŠธ๋กค๋Ÿฌ ์•ˆ์—์„œ)

observeForever(Observer)๋ฅผ ์ด์šฉํ•˜์—ฌ LifecycleOwner ์—†์ด Observer๋ฅผ ๋“ฑ๋กํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ๊ฒฝ์šฐ Observer๊ฐ€ ํ•ญ์ƒ ํ™œ์„ฑํ™” ์ƒํƒœ๋กœ ์ธ์ง€๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋ณ€ํ™”์— ๋Œ€ํ•œ ์•Œ๋ฆผ์„ ํ•ญ์ƒ ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. removeObserver(Observer) ๋ฉ”์„œ๋“œ๋ฅผ ํ†ตํ•ด Observer๋ฅผ ์ œ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

LiveData ์ƒ์„ฑ

LiveData๋Š” Collections ๊ตฌํ˜„์ฒด๋ฅผ ํฌํ•จํ•œ ๋ชจ๋“  ํƒ€์ž…์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๋‹ด์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋ณดํ†ต LiveData๋Š” ์•„๋ž˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ ViewModel ์•ˆ์— ์ €์žฅ๋˜๋ฉฐ, getter๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ๋ฉ๋‹ˆ๋‹ค. ์ดˆ๊ธฐ์— LiveData ๊ฐ’์€ ์„ค์ •๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

public class NameViewModel extends ViewModel {

// String ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ–๋Š” LiveData
private MutableLiveData<String> currentName;

    public MutableLiveData<String> getCurrentName() {
        if (currentName == null) {
            currentName = new MutableLiveData<String>();
        }
        return currentName;
    }

// Rest of the ViewModel...
}
LiveData๋ฅผ Activity/Fragment๊ฐ€ ์•„๋‹Œ ViewModel์— ์ €์žฅํ•˜์„ธ์š”.

- Activity/Fragment ํฌ๊ธฐ๊ฐ€ ๋„ˆ๋ฌด ์ปค์ง€๋Š” ๊ฒƒ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด,
    - ์ด์ œ UI ์ปจํŠธ๋กค๋Ÿฌ์˜ ์—ญํ• ์€ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๋Š” ๊ฒƒ์ด์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒƒ์ด ์•„๋‹™๋‹ˆ๋‹ค.
- Configuration change๊ฐ€ ๋ฐœ์ƒํ•ด๋„ LiveData๋Š” ์œ ์ง€๋˜๋„๋ก ํŠน์ • Activity/Fragment์™€ LiveData ์ธ์Šคํ„ด์Šค๋ฅผ ๋ถ„๋ฆฌํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

LiveData ๊ตฌ๋…ํ•˜๊ธฐ

๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ, ์•ฑ ์ปดํฌ๋„ŒํŠธ์˜ onCreate() ์‹œ์ ์ด ๊ตฌ๋…์„ ์‹œ์ž‘ํ•˜๊ธฐ์— ์ ์ ˆํ•ฉ๋‹ˆ๋‹ค.

  • Activity/Fragment ์˜ onReume()์— ์ •์˜ํ–ˆ์„ ๋•Œ์˜ ๋ถˆํ•„์š”ํ•œ ํ˜ธ์ถœ์„ ๋ฐฉ์ง€ํ•˜๊ธฐ ์œ„ํ•ด
  • Activity/Fragment๊ฐ€ ํ™œ์„ฑํ™”๋œ ํ›„ ์ตœ๋Œ€ํ•œ ๋นจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฅผ ํ‘œ์‹œํ•˜๊ธฐ ์œ„ํ•ด

๊ตฌ๋… ์‹œ์ž‘์— ๋Œ€ํ•œ ์˜ˆ์ œ ์ฝ”๋“œ:

public class NameActivity extends AppCompatActivity {

    private NameViewModel model;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Other code to setup the activity...

        // Get the ViewModel.
        model = ViewModelProviders.of(this).get(NameViewModel.class);


        // Create the observer which updates the UI.
        final Observer<String> nameObserver = new Observer<String>() {
            @Override
            public void onChanged(@Nullable final String newName) {
                // Update the UI, in this case, a TextView.
                nameTextView.setText(newName);
            }
        };

        // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
        model.getCurrentName().observe(this, nameObserver);
    }
}

nameObserver.observe()๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉด mCurrentName์˜ ๊ฐ€์žฅ ์ตœ์‹  ๊ฐ’์„ ์ฆ‰์‹œ onChanged()๋กœ ์ „๋‹ฌ๋ฐ›๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. LiveData์— ๊ฐ’์ด ์•„์ง ์„ค์ •๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด onChanged()๋Š” ํ˜ธ์ถœ๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

LiveData ์—…๋ฐ์ดํŠธ

LiveData๋Š” ์ €์žฅ๋œ ๊ฐ’์„ ๊ฐฑ์‹ ํ•˜๋Š” public ํ•จ์ˆ˜๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. LiveData์— ์ €์žฅ๋œ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ํ•„์š”๊ฐ€ ์žˆ๋Š” ๊ฒฝ์šฐ setValue(T)/postValue(T)๋ฅผ ์ œ๊ณตํ•˜๋Š” MutableLiveData๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๋ณดํ†ต MutableLiveData๋Š” ViewModel ์•ˆ์—์„œ๋งŒ ์‚ฌ์šฉํ•˜๊ณ , ViewModel์€ Observer์— ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€๋Šฅํ•œ LiveData๋งŒ ๊ณต๊ฐœํ•ฉ๋‹ˆ๋‹ค.

LiveData ์—…๋ฐ์ดํŠธ ์˜ˆ์ œ:

button.setOnClickListener(new OnClickListener() {
    @Override
    public void onClick(View v) {
        String anotherName = "John Doe";
        model.getCurrentName().setValue(anotherName);
    }
});
๋ฉ”์ธ ์Šค๋ ˆ๋“œ์—์„œ LiveData๋ฅผ ์—…๋ฐ์ดํŠธ ํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” ๋ฐ˜๋“œ์‹œ setValue(T) ์‚ฌ์šฉ
๋งŒ์•ฝ ์›Œ์ปค ์Šค๋ ˆ๋“œ์—์„œ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค๋ฉด postValue(T) ์‚ฌ์šฉ

Room๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ

Room ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋Š” LiveData๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” Observable ์ฟผ๋ฆฌ๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. Room์€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ๋ณ€๊ฒฝ๋์„ ๋•Œ LiveData๋ฅผ ์—…๋ฐ์ดํŠธํ•˜๋Š” ๋ฐ ํ•„์š”ํ•œ ๋ชจ๋“  ์ฝ”๋“œ๋ฅผ ์ƒ์„ฑํ•ด์ค๋‹ˆ๋‹ค. ์ƒ์„ฑ๋œ ์ฝ”๋“œ๋Š” ํ•„์š”ํ•œ ๊ฒฝ์šฐ ๋ฐฑ๊ทธ๋ผ์šด๋“œ์—์„œ ๋น„๋™๊ธฐ์ ์œผ๋กœ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์˜ ๋ฐ์ดํ„ฐ์™€ UI๋ฅผ ํ•ญ์ƒ ๋™๊ธฐ์ ์œผ๋กœ ์œ ์ง€ํ•˜๊ธฐ์— ์œ ์šฉํ•ฉ๋‹ˆ๋‹ค. Room๊ณผ DAO์— ๋Œ€ํ•ด์„œ๋Š” Room ๊ฐ€์ด๋“œ ์—์„œ ๋” ์ž์„ธํžˆ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Coroutine๊ณผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜๊ธฐ

LiveData๋Š” Kotlin coroutines๋„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ Android App Component์™€ Kotlin coroutine ์‚ฌ์šฉํ•˜๊ธฐ ์ฐธ๊ณ 

LiveData ํ™•์žฅ

public class StockLiveData extends LiveData<BigDecimal> {
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    public StockLiveData(String symbol) {
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
        stockManager.removeUpdates(listener);
    }
}
  • onActive(): ํ™œ์„ฑํ™” Observer๊ฐ€ ์žˆ์„ ๋•Œ ํ˜ธ์ถœ. ์ฃผ์‹ ๊ฐ€๊ฒฉ ๋ณ€ํ™”์— ๋Œ€ํ•œ ๊ตฌ๋…์„ ์ด ๋ฉ”์„œ๋“œ์—์„œ ์‹œ์ž‘ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
  • onInactive(): ํ™œ์„ฑํ™” Observer๊ฐ€ ํ•˜๋‚˜๋„ ์—†์„ ๋•Œ ํ˜ธ์ถœ. Observer๊ฐ€ ํ•˜๋‚˜๋„ ์—†์œผ๋ฏ€๋กœ StockManager์™€ ์—ฐ๊ฒฐ์„ ์œ ์ง€ํ•  ํ•„์š”๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.
  • setValue(T) LiveData๊ฐ€ Lifecycle์„ ์•Œ๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์—ฌ๋Ÿฌ Activity/Fragment/Service ๊ฐ„ ๊ณต์œ ๊ฐ€ ๊ฐ€๋Šฅํ•ฉ๋‹ˆ๋‹ค.

LiveData๋ฅผ ์‹ฑ๊ธ€ํ†ค์œผ๋กœ ๋งŒ๋“  ์˜ˆ์ œ:

public class StockLiveData extends LiveData<BigDecimal> {
    private static StockLiveData sInstance;
    private StockManager stockManager;

    private SimplePriceListener listener = new SimplePriceListener() {
        @Override
        public void onPriceChanged(BigDecimal price) {
            setValue(price);
        }
    };

    @MainThread
    public static StockLiveData get(String symbol) {
        if (sInstance == null) {
            sInstance = new StockLiveData(symbol);
        }
        return sInstance;
    }

    private StockLiveData(String symbol) {
        stockManager = new StockManager(symbol);
    }

    @Override
    protected void onActive() {
        stockManager.requestPriceUpdates(listener);
    }

    @Override
    protected void onInactive() {
        stockManager.removeUpdates(listener);
    }
}

์•„๋ž˜ ์˜ˆ์ œ์ฒ˜๋Ÿผ Fragment์—์„œ ์‚ฌ์šฉ:

public class MyFragment extends Fragment {
    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        StockLiveData.get(symbol).observe(this, price -> {
            // Update the UI.
        });
    }
}

LiveData ๋ณ€ํ˜•

  • Observer์— ์ „๋‹ฌํ•˜๊ธฐ ์ „์— ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ
  • ๋‹ค๋ฅธ ๊ฐ’์— ๊ธฐ๋ฐ˜ํ•˜์—ฌ ๋‹ค๋ฅธ LiveData๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ

Lifecycle ํŒจํ‚ค์ง€๋Š” ์ด๋Ÿฐ ๊ฒฝ์šฐ๋ฅผ ์œ„ํ•œ Transformations ํด๋ž˜์Šค๋ฅผ ์ œ๊ณตํ•จ

Transformations.map()

LiveData์˜ ๊ฐ’์— ํ•จ์ˆ˜๋ฅผ ์ ์šฉ์‹œํ‚ค๊ณ , ๊ทธ ๊ฒฐ๊ณผ ๊ฐ’์„ ์ „๋‹ฌ

LiveData<User> userLiveData = ...;
LiveData<String> userName = Transformations.map(userLiveData, user -> {
    user.name + " " + user.lastName
});

Transformations.switchMap()

map()๊ณผ ์œ ์‚ฌํ•˜๊ฒŒ LiveData์˜ ๊ฐ’์— ํ•จ์ˆ˜๋ฅผ ์ ์šฉ์‹œํ‚ค๊ณ , ๊ทธ ๊ฒฐ๊ณผ ๊ฐ’์„ ์ „๋‹ฌํ•˜์ง€๋งŒ, switchMap()์˜ ๋ฆฌํ„ด ํƒ€์ž…์€ LiveData

private LiveData<User> getUser(String id) {
  ...;
}

LiveData<String> userId = ...;
LiveData<User> user = Transformations.switchMap(userId, id -> getUser(id) );

Transformations๋Š” ๋ฐ˜ํ™˜๋œ LiveData๋ฅผ ๊ตฌ๋…ํ•˜๋Š” Observer๊ฐ€ ์—†์œผ๋ฉด ๋ณ€ํ˜•์„ ์ ์šฉํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. Transformations๋Š” lazy ์—ฐ์‚ฐ์ด๊ธฐ ๋•Œ๋ฌธ์—, Lifecycle๊ณผ ์—ฐ๊ด€๋œ ๋™์ž‘์€ ๋ช…์‹œ์ ์ธ ํ˜ธ์ถœ์ด๋‚˜ ์˜์กด์„ฑ์˜ ํ•„์š”์—†์ด ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

ViewModel ๊ฐ์ฒด ์•ˆ์—์„œ LiveData๊ฐ€ ํ•„์š”ํ•œ ๊ฒฝ์šฐ, Transformations๋Š” ๋” ์ข‹์€ ๋ฐฉ์•ˆ์ด ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. UI ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์ฃผ์†Œ์™€ ๊ทธ ์ฃผ์†Œ์— ํ•ด๋‹นํ•˜๋Š” ์šฐํŽธ ๋ฒˆํ˜ธ๋ฅผ ๋ฐ›๋Š”๋‹ค๊ณ  ๊ฐ€์ •ํ•  ๋•Œ, ์ž˜ ๋ชจ๋ฅด๊ณ  ์ž‘์„ฑํ•œ ViewModel ์ฝ”๋“œ๋Š” ์•„๋ž˜์™€ ๊ฐ™์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    public MyViewModel(PostalCodeRepository repository) {
       this.repository = repository;
    }

    private LiveData<String> getPostalCode(String address) {
       // DON'T DO THIS
       return repository.getPostCode(address);
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด UI ์ปดํฌ๋„ŒํŠธ์—์„œ getPostalCode()๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์ด์ „ LiveData์˜ ๊ตฌ๋…์„ ํ•ด์ง€ํ•˜๊ณ , ์ƒˆ๋กœ์šด LiveData๋ฅผ ๊ตฌ๋…ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ฒŒ๋‹ค๊ฐ€ UI ์ปดํฌ๋„ŒํŠธ๊ฐ€ ์žฌ์ƒ์„ฑ๋์„ ๋•Œ, ์ด์ „ ํ˜ธ์ถœ์˜ ๊ฒฐ๊ณผ๋ฅผ ์‚ฌ์šฉํ•˜์ง€ ์•Š๊ณ  repository.getPostCode()๋ฅผ ๋˜ ๋‹ค์‹œ ํ˜ธ์ถœํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ฃผ์†Œ์— ๋”ฐ๋ผ ์šฐํŽธ ๋ฒˆํ˜ธ๋ฅผ ์ฐพ๋Š” ๋กœ์ง์„ Transformations๋ฅผ ์ด์šฉํ•˜์—ฌ ์•„๋ž˜์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค:

class MyViewModel extends ViewModel {
    private final PostalCodeRepository repository;
    private final MutableLiveData<String> addressInput = new MutableLiveData();
    public final LiveData<String> postalCode =
            Transformations.switchMap(addressInput, (address) -> {
                return repository.getPostCode(address);
             });

  public MyViewModel(PostalCodeRepository repository) {
      this.repository = repository
  }

  private void setInput(String address) {
      addressInput.setValue(address);
  }
}

postalCode๋Š” addressInput์˜ ๋ณ€ํ˜•์œผ๋กœ ์ •์˜๋ฉ๋‹ˆ๋‹ค. postalCode์™€ ์—ฐ๊ฒฐ๋œ ํ™œ์„ฑํ™” ์ƒํƒœ์˜ Observer๊ฐ€ ์žˆ๊ณ , addressInput์ด ๋ณ€๊ฒฝ๋˜๋ฉด postalCode์˜ ๊ฐ’์„ ๋‹ค์‹œ ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค.

์ƒˆ๋กœ์šด ๋ณ€ํ˜• ์ƒ์„ฑํ•˜๊ธฐ

์•„์ฃผ ๋‹ค์–‘ํ•œ ๋ณ€ํ˜• ๋ฐฉ๋ฒ•์ด ์žˆ์ง€๋งŒ, ๋””ํดํŠธ๋กœ ์ œ๊ณต๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์›ํ•˜๋Š” ๋ณ€ํ˜•์„ ๊ตฌํ˜„ํ•˜๊ณ  ์‹ถ์€ ๊ฒฝ์šฐ MediatorLiveData๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ https://developer.android.com/reference/androidx/lifecycle/Transformations.html?hl=ko

์—ฌ๋Ÿฌ LiveData ํ•ฉ์น˜๊ธฐ

MediatorLiveData๋Š” LiveData์˜ ์„œ๋ธŒํด๋ž˜์Šค๋กœ, ์—ฌ๋Ÿฌ LiveData๋ฅผ ํ•ฉ์น  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค๋‹ˆ๋‹ค. MediatorLiveData๋Š” LiveData๋“ค ์ค‘ ํ•˜๋‚˜๋ผ๋„ ๋ณ€๊ฒฝ๋˜๋ฉด Observer์— ๋ณ€๊ฒฝ์„ ์•Œ๋ฆฝ๋‹ˆ๋‹ค.

์˜ˆ, ๋กœ์ปฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ๋˜๋Š” ๋„คํŠธ์›Œํฌ๊ฐ€ ๋ณ€๊ฒฝ๋  ๋•Œ UI๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค๋ฉด, ์•„๋ž˜์ฒ˜๋Ÿผ MediatorLiveData์— LiveData๋ฅผ ์ถ”๊ฐ€ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ LiveData
  • ๋„คํŠธ์›Œํฌ๋กœ ์ ‘๊ทผํ•˜๋Š” ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ LiveData

๋” ์ž์„ธํ•œ ์˜ˆ์ œ๋Š” App Architecture ๊ฐ€์ด๋“œ์˜ Addendum: exposing network status ํŒŒํŠธ ์ฐธ๊ณ 

์ถ”๊ฐ€ ์ž๋ฃŒ

Samples

Codelabs

Blogs

Videos

Comments