From 3914a183809310f2705eb1edec5100f1df836ad8 Mon Sep 17 00:00:00 2001 From: Ruslan Sabirov Date: Mon, 26 Jan 2026 00:31:43 +0300 Subject: [PATCH 1/3] =?UTF-8?q?=D0=97=D0=B0=D0=BC=D0=B5=D0=BD=D0=B8=D0=BB?= =?UTF-8?q?=20LiveData=20=D0=BD=D0=B0=20StateFlow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- flowcats/build.gradle | 5 +++-- .../otus/homework/flowcats/CatsRepository.kt | 3 +++ .../java/otus/homework/flowcats/CatsView.kt | 6 ++++++ .../otus/homework/flowcats/CatsViewModel.kt | 19 +++++++++++------ .../main/java/otus/homework/flowcats/Fact.kt | 18 ++++++++-------- .../otus/homework/flowcats/MainActivity.kt | 21 +++++++++++++++++-- .../java/otus/homework/flowcats/Result.kt | 6 ++++++ 7 files changed, 59 insertions(+), 19 deletions(-) create mode 100644 flowcats/src/main/java/otus/homework/flowcats/Result.kt diff --git a/flowcats/build.gradle b/flowcats/build.gradle index 0ea35e68..3049be1b 100644 --- a/flowcats/build.gradle +++ b/flowcats/build.gradle @@ -4,7 +4,7 @@ plugins { } android { - compileSdkVersion 30 + compileSdkVersion 36 buildToolsVersion "30.0.3" namespace = "otus.homework.flowcats" @@ -47,5 +47,6 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3' implementation 'androidx.activity:activity-ktx:1.2.3' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.4.3' - implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.0' + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.0" } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt index 10fcb77d..6103b1e6 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.catch import kotlinx.coroutines.flow.flow class CatsRepository( @@ -14,5 +15,7 @@ class CatsRepository( emit(latestNews) delay(refreshIntervalMs) } + }.catch { throwable -> + throw throwable } } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt index 6a195f3a..b94aff78 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsView.kt @@ -3,6 +3,7 @@ package otus.homework.flowcats import android.content.Context import android.util.AttributeSet import android.widget.TextView +import android.widget.Toast import androidx.constraintlayout.widget.ConstraintLayout class CatsView @JvmOverloads constructor( @@ -14,9 +15,14 @@ class CatsView @JvmOverloads constructor( override fun populate(fact: Fact) { findViewById(R.id.fact_textView).text = fact.text } + + override fun showError(message: String) { + Toast.makeText(context, message, Toast.LENGTH_LONG).show() + } } interface ICatsView { fun populate(fact: Fact) + fun showError(message: String) } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt index 0d8ba8a7..b9e1b32c 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -2,6 +2,9 @@ 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.collect import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -10,22 +13,26 @@ class CatsViewModel( private val catsRepository: CatsRepository ) : ViewModel() { - private val _catsLiveData = MutableLiveData() - val catsLiveData: LiveData = _catsLiveData + private val _catsStateFlow = MutableStateFlow>(Result.Success(Fact())) + val catsStateFlow: StateFlow> = _catsStateFlow.asStateFlow() init { viewModelScope.launch { - withContext(Dispatchers.IO) { - catsRepository.listenForCatFacts().collect { - _catsLiveData.value = it + try { + catsRepository.listenForCatFacts().collect { fact -> + _catsStateFlow.value = Result.Success(fact) } + } catch (e: Exception) { + _catsStateFlow.value = Result.Error(e) } } } } + 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 diff --git a/flowcats/src/main/java/otus/homework/flowcats/Fact.kt b/flowcats/src/main/java/otus/homework/flowcats/Fact.kt index 602303eb..d4c4978a 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/Fact.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/Fact.kt @@ -4,21 +4,21 @@ import com.google.gson.annotations.SerializedName data class Fact( @field:SerializedName("createdAt") - val createdAt: String, + val createdAt: String = "", @field:SerializedName("deleted") - val deleted: Boolean, + val deleted: Boolean = false, @field:SerializedName("_id") - val id: String, + val id: String = "", @field:SerializedName("text") - val text: String, + val text: String = "", @field:SerializedName("source") - val source: String, + val source: String = "", @field:SerializedName("used") - val used: Boolean, + val used: Boolean = false, @field:SerializedName("type") - val type: String, + val type: String = "", @field:SerializedName("user") - val user: String, + val user: String = "", @field:SerializedName("updatedAt") - val updatedAt: String + val updatedAt: String = "" ) \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt index edea434b..10dc29d3 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/MainActivity.kt @@ -3,6 +3,10 @@ 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 androidx.lifecycle.repeatOnLifecycle +import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { @@ -14,8 +18,21 @@ class MainActivity : AppCompatActivity() { 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.catsStateFlow.collect { result -> + when (result) { + is Result.Success -> { + view.populate(result.data) + } + is Result.Error -> { + view.showError(result.throwable?.message ?: "Unknown error!") + } + } + } + } } + + } } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/Result.kt b/flowcats/src/main/java/otus/homework/flowcats/Result.kt new file mode 100644 index 00000000..2f59cc4d --- /dev/null +++ b/flowcats/src/main/java/otus/homework/flowcats/Result.kt @@ -0,0 +1,6 @@ +package otus.homework.flowcats + +sealed class Result { + data class Success(val data: T) : Result() + data class Error(val throwable: Throwable? = null) : Result() +} \ No newline at end of file From 0d689cb8947dfc7ba68e03fecde70af0108fec6c Mon Sep 17 00:00:00 2001 From: Ruslan Sabirov Date: Mon, 26 Jan 2026 00:47:50 +0300 Subject: [PATCH 2/3] =?UTF-8?q?=D0=A0=D0=B5=D0=B0=D0=BB=D0=B8=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BB=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=B8?= =?UTF-8?q?=20=D1=81=20=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE?= =?UTF-8?q?=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5=20flow=20=D0=BE=D0=BF=D0=B5?= =?UTF-8?q?=D1=80=D0=B0=D1=82=D0=BE=D1=80=D0=BE=D0=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../otus/homework/flow/SampleInteractor.kt | 34 ++++++++++++++++--- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt index 1993c064..be5d1d9f 100644 --- a/operators/src/main/java/otus/homework/flow/SampleInteractor.kt +++ b/operators/src/main/java/otus/homework/flow/SampleInteractor.kt @@ -18,7 +18,12 @@ class SampleInteractor( * 6) возвращает результат */ fun task1(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .map { it * 5 } // 1) умножаем на 5 + .filter { it > 20 } // 2) убираем числа <= 20 + .filter { it % 2 != 0 } // 3) убираем четные числа + .map { "$it won" } // 4) добавляем постфикс "won" + .take(3) // 5) берем 3 первых числа } /** @@ -29,7 +34,15 @@ class SampleInteractor( * Если число не делится на 3,5,15 - эмитим само число */ fun task2(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .flatMapConcat { number -> + when { + number % 15 == 0 -> flowOf(number.toString(), "FizzBuzz") + number % 5 == 0 -> flowOf(number.toString(), "Buzz") + number % 3 == 0 -> flowOf(number.toString(), "Fizz") + else -> flowOf(number.toString()) + } + } } /** @@ -38,7 +51,10 @@ class SampleInteractor( * Если айтемы в одно из флоу кончились то результирующий флоу также должен закончится */ fun task3(): Flow> { - return flowOf() + return sampleRepository.produceColors() + .zip(sampleRepository.produceForms()) { color, form -> + Pair(color, form) + } } /** @@ -48,6 +64,16 @@ class SampleInteractor( * При любом исходе, будь то выброс исключения или успешная отработка функции вызовите метод dotsRepository.completed() */ fun task4(): Flow { - return flowOf() + return sampleRepository.produceNumbers() + .catch { e -> + if (e is IllegalArgumentException) { + emit(-1) + } else { + throw e // пробрасываем другие исключения дальше + } + } + .onCompletion { + sampleRepository.completed() + } } } \ No newline at end of file From 778670c435340c4b2aa392912a2dfecf9c390d34 Mon Sep 17 00:00:00 2001 From: Ruslan Sabirov Date: Sun, 1 Feb 2026 01:01:19 +0300 Subject: [PATCH 3/3] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D0=BB?= =?UTF-8?q?=20=D0=BE=D0=B1=D1=80=D0=B0=D0=BE=D1=82=D0=BA=D1=83=20Cancellat?= =?UTF-8?q?ionException?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../otus/homework/flowcats/CatsRepository.kt | 16 ++++++++++------ .../java/otus/homework/flowcats/CatsViewModel.kt | 5 ++++- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt index 6103b1e6..2f048308 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsRepository.kt @@ -1,6 +1,8 @@ package otus.homework.flowcats +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.delay +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.flow @@ -10,12 +12,14 @@ class CatsRepository( ) { fun listenForCatFacts() = flow { - while (true) { - val latestNews = catsService.getCatFact() - emit(latestNews) - delay(refreshIntervalMs) + try { + while (true) { + val latestNews = catsService.getCatFact() + emit(latestNews) + delay(refreshIntervalMs) + } + } catch (e: CancellationException) { + throw e } - }.catch { throwable -> - throw throwable } } \ No newline at end of file diff --git a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt index b9e1b32c..8d24f4ce 100644 --- a/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt +++ b/flowcats/src/main/java/otus/homework/flowcats/CatsViewModel.kt @@ -1,6 +1,7 @@ package otus.homework.flowcats import androidx.lifecycle.* +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow @@ -22,8 +23,10 @@ class CatsViewModel( catsRepository.listenForCatFacts().collect { fact -> _catsStateFlow.value = Result.Success(fact) } + } catch (e: CancellationException) { + throw e } catch (e: Exception) { - _catsStateFlow.value = Result.Error(e) + _catsStateFlow.value = Result.Error(e) } } }