diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt index 10fcb77d..274949ae 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt @@ -1,6 +1,7 @@ package otus.homework.flowcats import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow class CatsRepository( @@ -8,10 +9,14 @@ class CatsRepository( private val refreshIntervalMs: Long = 5000 ) { - fun listenForCatFacts() = flow { + fun listenForCatFacts(): Flow> = flow { while (true) { - val latestNews = catsService.getCatFact() - emit(latestNews) + try { + emit(Result.Success(catsService.getCatFact())) + } catch (e: Exception) { + emit(Result.Error(e)) + break + } delay(refreshIntervalMs) } } diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt index 0d8ba8a7..3de504c7 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -2,7 +2,12 @@ package otus.homework.flowcats import androidx.lifecycle.* import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -10,22 +15,28 @@ class CatsViewModel( private val catsRepository: CatsRepository ) : ViewModel() { - private val _catsLiveData = MutableLiveData() - val catsLiveData: LiveData = _catsLiveData + private val _catsState = + MutableStateFlow>(Result.Error(IllegalStateException("No data"))) + val catsState: StateFlow> = _catsState.asStateFlow() init { viewModelScope.launch { - withContext(Dispatchers.IO) { - catsRepository.listenForCatFacts().collect { - _catsLiveData.value = it + catsRepository.listenForCatFacts() + .collect { result -> + _catsState.value = result } - } } } } class CatsViewModelFactory(private val catsRepository: CatsRepository) : ViewModelProvider.NewInstanceFactory() { - override fun create(modelClass: Class): T = + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T = CatsViewModel(catsRepository) as T -} \ No newline at end of file +} + +sealed class Result { + data class Success(val data: T) : Result() + data class Error(val throwable: Throwable) : Result() +} diff --git a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt index edea434b..65dff41e 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt @@ -3,19 +3,39 @@ package otus.homework.flowcats import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.activity.viewModels +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.lifecycleScope +import kotlinx.coroutines.InternalCoroutinesApi +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch +//import androidx.lifecycle.repeatOnLifecycle class MainActivity : AppCompatActivity() { private val diContainer = DiContainer() private val catsViewModel by viewModels { CatsViewModelFactory(diContainer.repository) } + @OptIn(InternalCoroutinesApi::class) override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) val view = layoutInflater.inflate(R.layout.activity_main, null) as CatsView setContentView(view) - catsViewModel.catsLiveData.observe(this){ - view.populate(it) + + lifecycleScope.launch { +// repeatOnLifecycle(Lifecycle.State.STARTED) { + catsViewModel.catsState.collect { result -> + when (result) { + is Result.Success -> view.populate(result.data) + is Result.Error -> { + // минимально: лог или заглушка + // Log.e("MainActivity", "Error", result.throwable) + } + } + } +// } } + } } \ No newline at end of file diff --git a/operators/build.gradle b/operators/build.gradle index 73cc3928..02e5e9d3 100644 --- a/operators/build.gradle +++ b/operators/build.gradle @@ -40,6 +40,7 @@ dependencies { implementation 'com.squareup.picasso:picasso:2.71828' implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3' implementation 'androidx.activity:activity-ktx:1.2.3' +// implementation "androidx.lifecycle:lifecycle-runtime-ktx" testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.3' implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' } \ No newline at end of file diff --git a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt index 1993c064..20e6daf5 100644 --- a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt +++ b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt @@ -1,6 +1,7 @@ package otus.homework.flow import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.FlowPreview import kotlinx.coroutines.flow.* @ExperimentalCoroutinesApi @@ -18,7 +19,12 @@ class SampleInteractor( * 6) возвращает результат */ fun task1(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .map { it * 5 } + .filter { it > 20 } + .filter { it % 2 != 0 } + .map { "$it won" } + .take(3) } /** @@ -28,8 +34,20 @@ class SampleInteractor( * Если входное число делится на 15 - эмитим само число и после него эмитим строку FizzBuzz * Если число не делится на 3,5,15 - эмитим само число */ + @OptIn(FlowPreview::class) fun task2(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .flatMapConcat { number -> + flow { + emit(number.toString()) + + when { + number % 15 == 0 -> emit("FizzBuzz") + number % 3 == 0 -> emit("Fizz") + number % 5 == 0 -> emit("Buzz") + } + } + } } /** @@ -38,7 +56,10 @@ class SampleInteractor( * Если айтемы в одно из флоу кончились то результирующий флоу также должен закончится */ fun task3(): Flow> { - return flowOf() + return sampleRepository.produceColors() + .zip(sampleRepository.produceForms()) { color, form -> + color to form + } } /** @@ -48,6 +69,17 @@ class SampleInteractor( * При любом исходе, будь то выброс исключения или успешная отработка функции вызовите метод dotsRepository.completed() */ fun task4(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .catch { throwable -> + if (throwable is IllegalArgumentException) { + emit(-1) + } else { + throw throwable + } + } + .onCompletion { + sampleRepository.completed() + } } -} \ No newline at end of file +} +