Skip to content

Instantly share code, notes, and snippets.

@houssemzaier
Created September 6, 2020 23:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save houssemzaier/f76d5886753170c4c3d798f78b2a71fb to your computer and use it in GitHub Desktop.
Save houssemzaier/f76d5886753170c4c3d798f78b2a71fb to your computer and use it in GitHub Desktop.
synchronized data observed with a Page sealed class pattern
package com.raywenderlich.kotlin.coroutines
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import androidx.lifecycle.*
import kotlinx.coroutines.*
import kotlinx.coroutines.test.runBlockingTest
import kotlinx.coroutines.test.setMain
import org.junit.Assert.assertEquals
import org.junit.Rule
import org.junit.Test
import kotlin.coroutines.CoroutineContext
@ExperimentalCoroutinesApi
class Main3 {
@Rule
@JvmField
var instantTaskExecutorRule = InstantTaskExecutorRule()
@Test
fun repositoryTest() = runBlockingTest {
val repository = Repository()
val loadContent1Result = repository.loadContent1()
assertEquals("content 1", loadContent1Result)
val loadContent2Result = repository.loadContent2()
assertEquals("content 2", loadContent2Result)
val loadContent3Result = repository.loadContent3()
assertEquals("content 3", loadContent3Result)
}
@Test
fun mainTest() = runBlockingTest {
val job1 = GlobalScope.launch(coroutineContext) {
val fragment = Fragment()
fragment.onActivityCreated(coroutineContext)
delay(66_666)
}
val job2 = GlobalScope.launch(coroutineContext) {
var timeout: Long = 20_000
while (timeout > 0) {
delay(1_000)
println("timeout ${timeout / 1_000} ")
timeout -= 1_000
}
}
joinAll(job1, job2)
}
@Test
fun main() = runBlocking {
Dispatchers.setMain(Dispatchers.Default)
val job1 = GlobalScope.launch {
val fragment = Fragment()
fragment.onActivityCreated()
}
val job2 = GlobalScope.launch {
var timeout: Long = 20_000
while (timeout > 0) {
delay(1_000)
println("timeout ${timeout / 1_000} ")
timeout -= 1_000
}
}
joinAll(job1, job2)
}
}
class Fragment : LifecycleOwner {
private val lifecycle = LifecycleRegistry(this)
init {
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
}
suspend fun onActivityCreated(_coroutineContext: CoroutineContext? = null) {
val repo = Repository()
val loadContentUseCase1 = LoadContentUseCase1(repo)
val loadContentUseCase2 = LoadContentUseCase2(repo)
val loadContentUseCase3 = LoadContentUseCase3(repo)
val vm = ViewModel(
loadContentUseCase1,
loadContentUseCase2,
loadContentUseCase3
)
vm.loadData(_coroutineContext)
//solution2: expose a LiveData of DataStructure wrapper that wraps the needed data received in order
vm.content.observe(this) { content ->
when (content) {
is Page.Error -> {
println("no content it should show error page")
}
is Page.Loading -> {
println("all content is loading ...")
}
else -> {
println("all content is $content")
}
}
}
}
override fun getLifecycle(): Lifecycle = lifecycle
}
class ViewModel(private val useCase1: LoadContentUseCase1,
private val useCase2: LoadContentUseCase2,
private val useCase3: LoadContentUseCase3
) : CoroutineScope {
private val handler = CoroutineExceptionHandler { _, throwable ->
println("Main3 ParentCoroutine is handling $throwable")
}
private val job = SupervisorJob()
override val coroutineContext: CoroutineContext
get() = handler + job
private val _content = MutableLiveData<Page>()
val content: LiveData<Page> = _content
suspend fun loadData(_coroutineContext: CoroutineContext? = null) {
_content.value = Page.Loading
launch(_coroutineContext ?: coroutineContext) {
val useCase1Response = async { useCase1() }
println("useCase1Response $useCase1Response")
val useCase2Response = async { useCase2() }
println("useCase2Response $useCase2Response")
val useCase3Response = async { useCase3() }
println("useCase3Response $useCase3Response")
val content1 = useCase1Response.await()
val content2 = useCase2Response.await()
val content3 = useCase3Response.await()
if (content1.isEmpty() && content2.isEmpty() && content3.isEmpty()) {
_content.value = Page.Error
} else {
_content.value = Page.Content(content1, content2, content3)
}
}
}
fun onClear() {
job.cancel()
}
}
sealed class Page {
object Loading : Page()
object Error : Page()
data class Content(
val content1: String?,
val content2: String?,
val content3: String?,
) : Page()
}
class LoadContentUseCase1(private val repository: Repository) {
suspend operator fun invoke() = repository.loadContent1()
}
class LoadContentUseCase2(private val repository: Repository) {
suspend operator fun invoke() = repository.loadContent2()
}
class LoadContentUseCase3(private val repository: Repository) {
suspend operator fun invoke() = repository.loadContent3()
}
class Repository {
suspend fun loadContent1(): String {
println("loadContent1 is started")
delay(1000)
println("loadContent1 is finished")
return "content 1"
}
suspend fun loadContent2(): String {
println("loadContent2 is started")
delay(2000)
println("loadContent2 is finished")
return "content 2"
}
suspend fun loadContent3(): String {
println("loadContent3 is started")
delay(3000)
println("loadContent3 is finished")
return "content 3"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment