rokkonet

PC・Androidソフトウェア・アプリの開発・使い方に関するメモ

android開発 ViewModel内のコルーチン内の結果でActivityの動作を決めるには。Boolean型suspend関数の結果をActivityに渡す方法

2021 Sep. 05.
2021 Sep. 03.

参考ページ android ViewModelScopeコルーチンでメインスレッドを停止する方法は? - スタック・オーバーフロー

コルーチンのlaunchブロック内のデータをlauchブロック外に渡すことはできない。 コルーチン内の結果で返り値を決めるBoolean型関数は作れない。

次のコードは不可。launch{}の終了を待たず"return retValue"が返される。

fun foo(): Boolean {
    lateinit val retValue: Boolean
    launch(Dispatchers..Default)  {
        retValue = BAR
    }
    return retValue
}


ViewModel内のコルーチン内の結果でActivityの動作を決める場合は、コルーチン内の結果をLiveDataとし、そのLiveDataをActivity内で受け取ってActivityの動作を決める。
下記コードでは、PassResultViewModel内のworkLongTime()メソッドの真偽をLiveDataのisFinishSuccessに格納し、それをMainActivityで読み込み、isFinishWorkに格納している。

MainActivity.kt

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val textViewOutput = findViewById<TextView>(R.id.textViewOutput)

        var isFinishWork: Boolean = false

        // get ViewModel
        val smbViewModelFactory = PassResultViewModelFactory()
        val passResultViewModel: PassResultViewModel = ViewModelProvider(this, smbViewModelFactory).get(PassResultViewModel::class.java)

        // Create Observer
        val isFinishSuccessObserver = Observer<Boolean> { isFinish ->
            isFinishWork = isFinish
            if (isFinishWork) {
                textViewOutput.setText(R.string._true)
            } else {
                textViewOutput.setText(R.string._false)
            }
        }

        // work in ViewModel
        passResultViewModel.startWork()

        // set result of method in ViewModel to variable
          // passResultViewModel.workLongTime(): Booleanの結果が、workLongTime()を呼び出したpassResultViewModel.startWork()の実行後にisFinishWorkに格納される
        passResultViewModel.isFinishSuccess.observe(this, isFinishSuccessObserver)
    }
}


PassResultViewModel.kt

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.*

class PassResultViewModel: ViewModel() {

    // LiveDataを設定する
    private var _isFinishSuccess = MutableLiveData<Boolean>(false)
    val isFinishSuccess: LiveData<Boolean>
        get() = _isFinishSuccess

    fun startWork() {
        viewModelScope.launch(Dispatchers.Default) {
            _isFinishSuccess.postValue(workLongTime())
        }
    }

    suspend fun workLongTime(): Boolean {
        delay(10000L)
        return true
    }

    override fun onCleared() {
        super.onCleared()
        viewModelScope.cancel()
    }
}


PassResultViewModelFactory.kt

import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider

class PassResultViewModelFactory: ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        return PassResultViewModel() as T
    }
}