From 1636db5fd8cce1a9ac129bbdfca03a4e033e8c09 Mon Sep 17 00:00:00 2001 From: Bogdan Patseev Date: Sun, 25 May 2025 00:17:49 +0300 Subject: [PATCH 1/5] add rx adapter --- app/build.gradle | 1 + app/src/main/java/otus/homework/reactivecats/CatsService.kt | 3 ++- app/src/main/java/otus/homework/reactivecats/DiContainer.kt | 4 ++++ 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 71df92dd..315f1a80 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -37,6 +37,7 @@ dependencies { implementation 'androidx.core:core-ktx:1.12.0' implementation 'com.squareup.retrofit2:retrofit:2.9.0' implementation 'com.squareup.retrofit2:converter-gson:2.9.0' + implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0' implementation 'com.google.code.gson:gson:2.10' implementation 'androidx.appcompat:appcompat:1.6.1' implementation 'com.google.android.material:material:1.11.0' diff --git a/app/src/main/java/otus/homework/reactivecats/CatsService.kt b/app/src/main/java/otus/homework/reactivecats/CatsService.kt index c79be483..52626c78 100644 --- a/app/src/main/java/otus/homework/reactivecats/CatsService.kt +++ b/app/src/main/java/otus/homework/reactivecats/CatsService.kt @@ -1,10 +1,11 @@ package otus.homework.reactivecats +import io.reactivex.Single import retrofit2.Call import retrofit2.http.GET interface CatsService { @GET("random?animal_type=cat") - fun getCatFact(): Call + fun getCatFact(): Single } \ No newline at end of file diff --git a/app/src/main/java/otus/homework/reactivecats/DiContainer.kt b/app/src/main/java/otus/homework/reactivecats/DiContainer.kt index dfbb9a2b..d3cdb6a3 100644 --- a/app/src/main/java/otus/homework/reactivecats/DiContainer.kt +++ b/app/src/main/java/otus/homework/reactivecats/DiContainer.kt @@ -1,7 +1,10 @@ package otus.homework.reactivecats import android.content.Context +import io.reactivex.internal.schedulers.RxThreadFactory +import io.reactivex.plugins.RxJavaPlugins import retrofit2.Retrofit +import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory import retrofit2.converter.gson.GsonConverterFactory class DiContainer { @@ -9,6 +12,7 @@ class DiContainer { private val retrofit by lazy { Retrofit.Builder() .baseUrl("https://cat-fact.herokuapp.com/facts/") + .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) .addConverterFactory(GsonConverterFactory.create()) .build() } From d085bed343711bf79369a7129097b752654d014b Mon Sep 17 00:00:00 2001 From: Bogdan Patseev Date: Sun, 25 May 2025 00:37:54 +0300 Subject: [PATCH 2/5] add rx chain --- .../homework/reactivecats/CatsViewModel.kt | 51 +++++++++++-------- 1 file changed, 30 insertions(+), 21 deletions(-) diff --git a/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt b/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt index d62eaf97..a5afbe08 100644 --- a/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt +++ b/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt @@ -1,14 +1,16 @@ package otus.homework.reactivecats +import android.annotation.SuppressLint import android.content.Context import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider -import retrofit2.Call -import retrofit2.Callback -import retrofit2.Response +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +@SuppressLint("CheckResult") class CatsViewModel( catsService: CatsService, localCatFactsGenerator: LocalCatFactsGenerator, @@ -17,28 +19,34 @@ class CatsViewModel( private val _catsLiveData = MutableLiveData() val catsLiveData: LiveData = _catsLiveData + val disposable = CompositeDisposable() init { - catsService.getCatFact().enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - if (response.isSuccessful && response.body() != null) { - _catsLiveData.value = Success(response.body()!!) - } else { - _catsLiveData.value = Error( - response.errorBody()?.string() ?: context.getString( - R.string.default_error_text - ) - ) - } - } - - override fun onFailure(call: Call, t: Throwable) { - _catsLiveData.value = ServerError - } - }) + disposable.add( + catsService.getCatFact() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { fact -> + _catsLiveData.value = Success(fact) + }, + { error -> + if (error.message?.contains("HTTP 5") ?: false) { + _catsLiveData.value = ServerError + } else { + _catsLiveData.value = Error(error.message.toString()) + } + } + ) + ) } fun getFacts() {} + + override fun onCleared() { + super.onCleared() + disposable.clear() + } } class CatsViewModelFactory( @@ -52,7 +60,8 @@ class CatsViewModelFactory( CatsViewModel(catsRepository, localCatFactsGenerator, context) as T } + sealed class Result data class Success(val fact: Fact) : Result() data class Error(val message: String) : Result() -object ServerError : Result() \ No newline at end of file +object ServerError : Result() From 6fc91844c1591a508400f3f42674f5d960fbc6c2 Mon Sep 17 00:00:00 2001 From: Bogdan Patseev Date: Sun, 25 May 2025 00:53:17 +0300 Subject: [PATCH 3/5] add generateCatFact --- .../otus/homework/reactivecats/LocalCatFactsGenerator.kt | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/otus/homework/reactivecats/LocalCatFactsGenerator.kt b/app/src/main/java/otus/homework/reactivecats/LocalCatFactsGenerator.kt index 4481062e..959989cd 100644 --- a/app/src/main/java/otus/homework/reactivecats/LocalCatFactsGenerator.kt +++ b/app/src/main/java/otus/homework/reactivecats/LocalCatFactsGenerator.kt @@ -15,7 +15,9 @@ class LocalCatFactsGenerator( * обернутую в подходящий стрим(Flowable/Single/Observable и т.п) */ fun generateCatFact(): Single { - return Single.never() + return Single.create { emitter -> + emitter.onSuccess(getRandomCatFact(context)) + } } /** @@ -27,4 +29,9 @@ class LocalCatFactsGenerator( val success = Fact(context.resources.getStringArray(R.array.local_cat_facts)[Random.nextInt(5)]) return Flowable.empty() } + + private fun getRandomCatFact(context: Context): Fact { + val localCatFacts = context.resources.getStringArray(R.array.local_cat_facts) + return Fact(localCatFacts[Random.nextInt(localCatFacts.size)]) + } } \ No newline at end of file From 8dc7d366d464ad52058a863ee91b2241eff77486 Mon Sep 17 00:00:00 2001 From: Bogdan Patseev Date: Sun, 25 May 2025 01:46:31 +0300 Subject: [PATCH 4/5] add generateCatFactPeriodically --- .../homework/reactivecats/CatsViewModel.kt | 6 ++- .../reactivecats/LocalCatFactsGenerator.kt | 46 ++++++++++++++++--- 2 files changed, 43 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt b/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt index a5afbe08..5eb470a5 100644 --- a/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt +++ b/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt @@ -9,6 +9,7 @@ import androidx.lifecycle.ViewModelProvider import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers +import java.util.concurrent.TimeUnit @SuppressLint("CheckResult") class CatsViewModel( @@ -23,7 +24,8 @@ class CatsViewModel( init { disposable.add( - catsService.getCatFact() + // catsService.getCatFact() + localCatFactsGenerator.generateCatFactPeriodically(context) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( @@ -45,7 +47,7 @@ class CatsViewModel( override fun onCleared() { super.onCleared() - disposable.clear() + disposable.dispose() } } diff --git a/app/src/main/java/otus/homework/reactivecats/LocalCatFactsGenerator.kt b/app/src/main/java/otus/homework/reactivecats/LocalCatFactsGenerator.kt index 959989cd..4ef6eec9 100644 --- a/app/src/main/java/otus/homework/reactivecats/LocalCatFactsGenerator.kt +++ b/app/src/main/java/otus/homework/reactivecats/LocalCatFactsGenerator.kt @@ -3,18 +3,22 @@ package otus.homework.reactivecats import android.content.Context import io.reactivex.Flowable import io.reactivex.Single +import java.util.concurrent.TimeUnit +import java.util.concurrent.atomic.AtomicReference import kotlin.random.Random class LocalCatFactsGenerator( private val context: Context ) { + private var atomicFact: AtomicReference = AtomicReference() + private val localCatFacts = context.resources.getStringArray(R.array.local_cat_facts) /** * Реализуйте функцию otus.homework.reactivecats.LocalCatFactsGenerator#generateCatFact так, * чтобы она возвращала Fact со случайной строкой из массива строк R.array.local_cat_facts * обернутую в подходящий стрим(Flowable/Single/Observable и т.п) */ - fun generateCatFact(): Single { + fun generateCatFact(context: Context): Single { return Single.create { emitter -> emitter.onSuccess(getRandomCatFact(context)) } @@ -25,13 +29,41 @@ class LocalCatFactsGenerator( * чтобы она эмитила Fact со случайной строкой из массива строк R.array.local_cat_facts каждые 2000 миллисекунд. * Если вновь заэмиченный Fact совпадает с предыдущим - пропускаем элемент. */ - fun generateCatFactPeriodically(): Flowable { - val success = Fact(context.resources.getStringArray(R.array.local_cat_facts)[Random.nextInt(5)]) - return Flowable.empty() - } + fun generateCatFactPeriodically(context: Context): Flowable = + Flowable + .interval(0, 2, TimeUnit.SECONDS) + .map { + getRandomCatFact(context) + }.distinctUntilChanged() + + + /** + * We don't have daley after emitting the same fact anymore + */ + fun generateCatFactPeriodicallySecondEdition(context: Context): Flowable = + Flowable + .interval(0, 2, TimeUnit.SECONDS) + .map { + getRandomCatFactSecondEdition(context, atomicFact) + } private fun getRandomCatFact(context: Context): Fact { - val localCatFacts = context.resources.getStringArray(R.array.local_cat_facts) - return Fact(localCatFacts[Random.nextInt(localCatFacts.size)]) + return Fact(localCatFacts[Random.nextInt(localCatFacts.size)]) + } + + private fun getRandomCatFactSecondEdition( + context: Context, + atomicFact: AtomicReference + ): Fact { + var newFact: Fact = getRandomCatFact(context) + if (atomicFact.get() == newFact) { + while (atomicFact.get() == newFact) { + newFact = getRandomCatFact(context) + } + atomicFact.set(newFact) + } else { + atomicFact.set(newFact) + } + return newFact } } \ No newline at end of file From 2bfb4509b1bb750a20e69a24bfcb5ac7b557fff5 Mon Sep 17 00:00:00 2001 From: Bogdan Patseev Date: Sun, 25 May 2025 16:16:17 +0300 Subject: [PATCH 5/5] define getFacts --- .../homework/reactivecats/CatsViewModel.kt | 42 +++++++++++-------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt b/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt index 5eb470a5..b02cad8d 100644 --- a/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt +++ b/app/src/main/java/otus/homework/reactivecats/CatsViewModel.kt @@ -6,6 +6,8 @@ import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import io.reactivex.Flowable + import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers @@ -23,27 +25,31 @@ class CatsViewModel( val disposable = CompositeDisposable() init { - disposable.add( - // catsService.getCatFact() - localCatFactsGenerator.generateCatFactPeriodically(context) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { fact -> - _catsLiveData.value = Success(fact) - }, - { error -> - if (error.message?.contains("HTTP 5") ?: false) { - _catsLiveData.value = ServerError - } else { - _catsLiveData.value = Error(error.message.toString()) - } - } - ) + getFacts( + catsService = catsService, + context = context, + localCatFactsGenerator = localCatFactsGenerator ) } - fun getFacts() {} + fun getFacts(catsService: CatsService, localCatFactsGenerator: LocalCatFactsGenerator, context: Context) { + val dis = Flowable.interval(0, 2, TimeUnit.SECONDS) + .flatMapSingle { + catsService.getCatFact() + .onErrorResumeNext { localCatFactsGenerator.generateCatFact(context) } + } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + _catsLiveData.value = Success(it) + }, + { + _catsLiveData.value = Error(it.message.toString()) + } + ) + disposable.add(dis) + } override fun onCleared() { super.onCleared()