Created
September 6, 2020 23:03
-
-
Save houssemzaier/f76d5886753170c4c3d798f78b2a71fb to your computer and use it in GitHub Desktop.
synchronized data observed with a Page sealed class pattern
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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