0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[LiveData] 更新されたときだけ値を受けとる

Last updated at Posted at 2020-04-21

二通りの方法を示します。

1. LiveData を BroadcastChannel に変換する

いきなりテストです。

class MyTest {
    private val dispatcher = TestCoroutineDispatcher()

    @[Rule JvmField]
    val rule = InstantTaskExecutorRule()

    @Before
    fun before() {
        Dispatchers.setMain(dispatcher)
    }

    @After
    fun after() {
        Dispatchers.resetMain()
        dispatcher.cleanupTestCoroutines()
    }

    @Test
    fun test() = dispatcher.runBlockingTest {
        // ソースとなる LiveData
        val source = object : MutableLiveData<Int>(0) {
            override fun onActive() = println("onActive")
            override fun onInactive() = println("onInactive")
        }

        // BroadcastChannel に変換
        val broadcastChannel = source.asFlow().broadcastIn(this)

        // Flow に変換 (都度 broadcastChannel.openSubscription() しても良い)
        val flow = broadcastChannel.asFlow()

        val job1 = launch { flow.collect { println("coroutine1: $it") } }

        source.value = 1

        job1.cancel()

        source.value = 2

        val job2 = launch { flow.collect { println("coroutine2: $it") } }

        source.value = 3

        job2.cancel()

        broadcastChannel.cancel()
    }
}

実行結果は以下です。

onActive
coroutine1: 0
coroutine1: 1
coroutine2: 3
onInactive

coroutine1 では初期値 0 および 1 を受信していますが、coroutine2 では 3 だけを受信しており、直前の値 2 を受信していないことがわかります。

利点

  • 特別な仕組みを作りこむ必要がない

欠点

  • おまじないっぽい?
  • 多くの関数が @ExperimentalCoroutinesApi だったり @FlowPreview だったりする

2. LiveData.getVersion() を使う

androidx.lifecycle パッケージ配下に以下の関数を用意します。

package androidx.lifecycle

internal fun LiveData<*>.version() = version

次に、任意のパッケージ配下に以下の関数を用意します。

@MainThread
fun <T> LiveData<T>.observeChangesForever(action: (T) -> Unit): Observer<T> =
    object : Observer<T> {
        private val version = version()
        override fun onChanged(t: T) {
            if (version() > version) action(t)
        }
    }.also { observeForever(it) }

省略していますが、LifecycleOwner を受け取る関数も同じように作ることができます。

テストは以下です。

class MyTest {
    @[Rule JvmField]
    val instantTaskExecutorRule: InstantTaskExecutorRule = InstantTaskExecutorRule()

    @Test
    fun test() {
        // ソースとなる LiveData
        val source = object : MutableLiveData<Int>(0) {
            override fun onActive() = println("onActive")
            override fun onInactive() = println("onInactive")
        }

        val observer1 = source.observeChangesForever { println("observer1: $it") }

        source.value = 1

        source.removeObserver(observer1)

        source.value = 2

        val observer2 = source.observeChangesForever { println("observer2: $it") }

        source.value = 3

        source.removeObserver(observer2)
    }
}

実行結果は以下です。

onActive
observer1: 1
onInactive
onActive
observer2: 3
onInactive

observer1observer2 共に直前の値 (0 および 2) を受信していないことがわかります。

利点

  • わかりやすい (かも)

欠点

  • 作りこみが必要
  • LiveData.getValue() はパッケージプライベートなので、今後動かなくなるかも

二通りの方法を示しましたが両者は挙動が異なるので、使い方には注意が必要です。

以上です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?