web-dev-qa-db-ja.com

MutableLiveData:コルーチンからバックグラウンドスレッドでsetValueを呼び出せません

コルーチンからLiveDataの更新をトリガーしようとしています:

object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
    AddressList.value = listOf()
    GlobalScope.launch {
        AddressList.value = getAddressList()
    }
    return AddressList
}

しかし、私は次のエラーを受け取ります:

IllegalStateException:バックグラウンドスレッドでsetValueを呼び出せない

コルーチンで動作させる方法はありますか?

17
kike

_liveData.value = value_の代わりにliveData.postValue(value)を使用します。非同期と呼ばれます。

ドキュメント から:

postValue-指定された値を設定するためにタスクをメインスレッドにポストします。

13

次のいずれかを実行できます。

object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
    AddressList.value = listOf()
    GlobalScope.launch {
        AddressList.postValue(getAddressList())
    }

return AddressList
}

または

fun getAddressesLiveData(): LiveData<List<Address>> {
    AddressList.value = listOf()
    GlobalScope.launch {
        val adresses = getAddressList()
        withContext(Dispatchers.Main) {
            AddressList.value = adresses
        }
    }
    return AddressList
}
11
pdegand59

私の場合、起動引数にDispatchers.Mainを追加する必要があり、正常に機能しました。

 val job = GlobalScope.launch(Dispatchers.Main) {
                    delay(1500)
                    search(query)
                }
3

他の人が指摘したように、この場合、ライブラリはメインスレッドに操作をポストする独自のメソッドを提供しますが、コルーチンは、特定のライブラリの機能に関係なく機能する一般的なソリューションを提供します。

最初のステップは、バックグラウンドジョブのGlobalScopeの使用を停止することです。これを行うと、アクティビティ、スケジュールされたジョブ、またはこれから呼び出したすべての作業単位が破壊され、さらにジョブがリークするリークが発生します。バックグラウンドで続行し、その結果をメインスレッドに送信します。 GlobalScopeの公式ドキュメント の内容は次のとおりです。

アプリケーションコードは通常、アプリケーション定義のCoroutineScopeを使用する必要があります。GlobalScopeのインスタンスで非同期または起動を使用することはお勧めしません。

独自のコルーチンスコープを定義し、そのcoroutineContextプロパティにディスパッチャとして_Dispatchers.Main_を含める必要があります。さらに、関数呼び出し内でジョブを起動してLiveData(基本的には別の種類のFuture)を返すパターン全体は、コルーチンを使用する最も便利な方法ではありません。代わりに

_suspend fun getAddresses() = withContext(Dispatchers.Default) { getAddressList() }
_

呼び出しサイトではコルーチンをlaunchする必要があります。コルーチン内で、ブロッキングメソッドのようにgetAddresses()を自由に呼び出して、アドレスを直接戻り値として取得できます。

3
Marko Topolnik

withContext(Dispatchers.Main){}を使用することで可能であることがわかりました。

object AddressList: MutableLiveData<List<Address>>()
fun getAddressesLiveData(): LiveData<List<Address>> {
    GlobalScope.launch {
        withContext(Dispatchers.Main){ AddressList.value = getAddressList() }
    }
    return AddressList
}
2
kike

Coroutinesを使用してUIを更新する場合、これを達成する方法は2つあります

GlobalScope.launch(Dispatchers.Main):

GlobalScope.launch(Dispatchers.Main) {
    delay(1000)     // 1 sec delay
    // call to UI thread
}

そして、いくつかの作業をバックグラウンドで実行したいが、その後Iを更新したい場合、これは次のようにして達成できます。

withContext(Dispatchers.Main)

GlobalScope.launch {
    delay(1000)     // 1 sec delay

    // do some background task

    withContext(Dispatchers.Main) {
            // call to UI thread
    }
}
1
Waqar UlHaq