axeldion
axeldion

Reputation: 99

Kotlin wait for async with coroutine

I would like to open a new activity when phoneViewModel and ScanViewModel are instantiated. They are instantiated by calling an async function InitialRead(). I'm logging each step, atm they are logged as done3 => done2 => done1

I would like to have them in this order: done1 => done2 => done3

I have following code:

class MainBusinessActivity : AppCompatActivity() {

private lateinit var scanViewModel: ScanViewModel
private lateinit var phoneViewModel: PhoneViewModel

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main_business)
}

private fun startEntitySetListActivity() = GlobalScope.async {
    val sapServiceManager = (application as SAPWizardApplication).sapServiceManager
    sapServiceManager?.openODataStore {
        phoneViewModel =  ViewModelProvider(this@MainBusinessActivity).get(PhoneViewModel::class.java).also {it.initialRead{Log.e("done", "done1")}}
        scanViewModel = ViewModelProvider(this@MainBusinessActivity).get(ScanViewModel::class.java).also {it.initialRead{Log.e("done", "done2")}}
    }
}

override fun onResume() {
    super.onResume()
    //startEntitySetListActivity()
    runBlocking {
        startEntitySetListActivity().await()
        val intent = Intent(this@MainBusinessActivity, HomeActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        Log.e("done", "done3")
        startActivity(intent)
    }
}

}

What am I doing wrong? Can someone correct my code?

Upvotes: 1

Views: 2139

Answers (1)

Tenfour04
Tenfour04

Reputation: 93511

Never use runBlocking in an Android app. runBlocking completely defeats the purpose of using coroutines, and can lead to an ANR. You also probably should never use GlobalScope, which leads to UI leaks. You might possibly need it for some kind of long-running task that doesn't make sense to put in a service but doesn't have dependency on any UI components, but I can't think of any examples

You also shouldn't be instantiating your ViewModels in the background. That should be done in onCreate().

Make this function a suspend function, and it can break down the two tasks in the background simultaneously before returning.

Start your coroutine with lifecycleScope.

Assuming sapServiceManager?.openODataStore is an asynchronous task that takes a callback, you will need to wrap it in suspendCoroutine.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main_business)
    phoneViewModel = ViewModelProvider(this@MainBusinessActivity).get(PhoneViewModel::class.java)
    scanViewModel = ViewModelProvider(this@MainBusinessActivity).get(ScanViewModel::class.java)
}

private suspend fun startEntitySetListActivity() = coroutineScope {
    val sapServiceManager = (application as SAPWizardApplication).sapServiceManager
    sapServiceManager ?: return
    suspendCoroutine<Unit> { continuation ->
        sapServiceManager.openODataStore { continuation.resume(Unit) }
    }
    listOf(
        launch {
            phoneViewModel.initialRead{Log.e("done", "done1")}
        },
        launch {
            scanViewModel.initialRead{Log.e("done", "done2")}
        }
    ).joinAll()
}

override fun onResume() {
    super.onResume()
    lifecycleScope.launch {
        startEntitySetListActivity()
        val intent = Intent(this@MainBusinessActivity, HomeActivity::class.java)
        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
        Log.e("done", "done3")
        startActivity(intent)
    }
}

Upvotes: 2

Related Questions