【MVVM】Transformations.switchMap() の使い方

👉 ViewModels and LiveData: Patterns + AntiPatterns – Android Developers – Medium 
👉 LiveData beyond the ViewModel — Reactive patterns using Transformations and MediatorLiveData 

LiveData をリアクティブに操作できるこのユーティリティメソッド。

LiveData を変換します。
これから返されるLiveData オブジェクトを Observe しておけば、Observer のライフサイクルを考慮しながら、データを変換することができます。lazy に処理され、呼び出しや依存関係の記述なしに、ライフサイクル関連の動作が引き継がれます。

👉 Transformations | android.arch.lifecycle.Transformations  |  Android Developers 

実体は MediatorLiveData のユーティリティ

ソースコードを見てみます。


@SuppressWarnings("WeakerAccess")
public class Transformations {

    private Transformations() {
    }

    @MainThread
    public static <X, Y> LiveData<Y> map(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, Y> mapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            @Override
            public void onChanged(@Nullable X x) {
                result.setValue(mapFunction.apply(x));
            }
        });
        return result;
    }

    @MainThread
    public static <X, Y> LiveData<Y> switchMap(
            @NonNull LiveData<X> source,
            @NonNull final Function<X, LiveData<Y>> switchMapFunction) {
        final MediatorLiveData<Y> result = new MediatorLiveData<>();
        result.addSource(source, new Observer<X>() {
            LiveData<Y> mSource;

            @Override
            public void onChanged(@Nullable X x) {
                LiveData<Y> newLiveData = switchMapFunction.apply(x);
                if (mSource == newLiveData) {
                    return;
                }
                if (mSource != null) {
                    result.removeSource(mSource);
                }
                mSource = newLiveData;
                if (mSource != null) {
                    result.addSource(mSource, new Observer<Y>() {
                        @Override
                        public void onChanged(@Nullable Y y) {
                            result.setValue(y);
                        }
                    });
                }
            }
        });
        return result;
    }
}

👉 Cross Reference: Transformations.java 

ソースコードより、これは、1:1 の MediatorLiveData を使うための便利ツールです。


class MainViewModel {
  val repositoryResult = Transformations.switchMap(userManager.user) { user →
     repository.getDataForUser(user)
  }
}

複数に変換したい場合は、直接 MediatorLiveData を使うのが良いでしょう。


val liveData1: LiveData<Int> = ...
val liveData2: LiveData<Int> = ...

val result = MediatorLiveData<Int>()

result.addSource(liveData1) { value →
    result.setValue(value)
}
result.addSource(liveData2) { value →
    result.setValue(value)
}

まとめ

ViewModel 内で、その下にある Repository との間で使います。

View (Activity / Fragment) のライフサイクルにも考慮されています。

ViewModel内で、データの id からその詳細情報の取得をしたり、検索文字列をフィルターとしてデータ抽出したり、などに使うことが多いようです。

👉 MediatorLiveDataとTransformationsでViewModelを効果的に使う 


Android Q Release Watcher


Android Q release date
- March 13: The first Android 10 developer beta has launched
- April 3: Android Q beta 2 gave us bugs fixes and app bubbles
- May 7: Android Q beta 3 launched at Google IO 2019
- Early June: The final incremental update, beta 4, should land in June
- July: Beta 5 and beta 6, release candidates, may land this month
- August: The final release has routinely happened in August

👉 Android Q release date, new features and everything you need to know | TechRadar 

Beta 4



Update 2 (6/6/19 @ 9:25 AM ET): Google has halted the Android Q Beta 4 OTA update due to bootlooping issues.

Update 2: OTA Halted
Numerous Pixel owners reported that their devices were bootlooping after installing the Android Q Beta 4 OTA update. It appeared to mostly affect Pixel 2 XL owners, but a few Pixel 3 owners also had the problem. A factory reset fixed things, but this is obviously a problem. Google has officially halted the OTA on all Pixel phones as they investigate the situation.

We’re aware of an issue with Android Q Beta 4 related to installing updates. We’ve temporarily paused Beta 4 OTA updates to all Pixel devices as we investigate the issue. We apologize for any inconvenience, and will provide an update once the issue is resolved.

👉 Android Q Beta 4 now available! : android_beta 

👉 Android Q beta4 のOTAが来ない理由 


AbsentLiveData とは?

あちこちで見かける AbsentLiveData。

ViewModel の中で以下のように使われています。


val game: LiveData<RefreshableResource<GameEntity>> = Transformations.switchMap(_gameId) { gameId ->
    when (gameId) {
        BggContract.INVALID_ID -> AbsentLiveData.create()
        else -> gameRepository.getGame(gameId)
    }
}

調べてみると、

どうやら、null の入った LiveData のようです。


/**
 * A LiveData class that has `null` value.
 */
class AbsentLiveData<T : Any?> private constructor(): LiveData<T>() {
    init {
        // use post instead of set since this can be created on any thread
        postValue(null)
    }

    companion object {
        fun <T> create(): LiveData<T> {
            return AbsentLiveData()
        }
    }
}

👉 android-architecture-components/AbsentLiveData.kt at master · googlesamples/android-architecture-components 

うん、揃う。

これは、コピペで使っていきましょう。