rxjava2: Using concatenated completables and observing them in IO thread - android

At first, I know that network operations shouldn`t be called from Main thread. Thats why I am observing completables on Schedulers.io()!
I am trying to concat two completable. Both completable use network, thats why i subscribe on Schedulers.io(). If i am using concatWith(or andThen) code fails with NetworkOnMainThreadException. Here is kotlin code:
val singleSubject = SingleSubject.create<String>();
completalbe1.concatWith(completable2)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe({
singleSubject.onSuccess("ok")
}, { error ->
Log.e(tag, error.message, error)//here i got exception
singleSubject.onError(error)
})
return singleSubject
If i rewrite code without completable chaining - all is ok. Here is working code:
val singleSubject = SingleSubject.create<String>();
completable1
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe({
completable2
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe({
singleSubject.onSuccess("ok")
}, { error ->
Log.e(tag, error.message, error)
singleSubject.onError(error)
})
}, {error ->
Log.e(tag, error.message, error)
singleSubject.onError(error)
})
return singleSubject
I wonder why first snippet don`t work but second does?
UPD1:Here is stacktrace:
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1273)
at libcore.io.BlockGuardOs.recvfrom(BlockGuardOs.java:249)
at libcore.io.IoBridge.recvfrom(IoBridge.java:549)
at java.net.PlainSocketImpl.read(PlainSocketImpl.java:481)
at java.net.PlainSocketImpl.access$000(PlainSocketImpl.java:37)
at java.net.PlainSocketImpl$PlainSocketInputStream.read(PlainSocketImpl.java:237)
at okio.Okio$2.read(Okio.java:139)
at okio.AsyncTimeout$2.read(AsyncTimeout.java:237)
at okio.RealBufferedSource.exhausted(RealBufferedSource.java:56)
at okhttp3.internal.connection.RealConnection.isHealthy(RealConnection.java:498)
at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:133)
at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:100)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:120)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:211)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall.execute(RealCall.java:69)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
at com.jakewharton.retrofit2.adapter.rxjava2.CallObservable.subscribeActual(CallObservable.java:41)
at io.reactivex.Observable.subscribe(Observable.java:10955)
at com.jakewharton.retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:10955)
at io.reactivex.internal.operators.observable.ObservableIgnoreElementsCompletable.subscribeActual(ObservableIgnoreElementsCompletable.java:31)
at io.reactivex.Completable.subscribe(Completable.java:1664)
at io.reactivex.internal.operators.completable.CompletableConcatArray$ConcatInnerObserver.next(CompletableConcatArray.java:89)
at io.reactivex.internal.operators.completable.CompletableConcatArray$ConcatInnerObserver.onComplete(CompletableConcatArray.java:65)
at io.reactivex.internal.operators.completable.CompletableCreate$Emitter.onComplete(CompletableCreate.java:64)
at com.catalyst.opti.AppManager$transferImage$1$subscribe$1.onStateChanged(AppManager.kt:323)
at com.amazonaws.mobileconnectors.s3.transferutility.TransferStatusUpdater$1.run(TransferStatusUpdater.java:172)
at android.os.Handler.handleCallback(Handler.java:742)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:5527)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:739)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:629)
UPD2:
completable1 is a function uploading file to AWS S3:
private fun transferImage(imageName: String, image: File): Completable {
return Completable.create(object : CompletableOnSubscribe {
override fun subscribe(e: CompletableEmitter) {
val transferObserver = transferUtility.upload("some", imageName, image)
transferObserver.setTransferListener(object : TransferListener {
override fun onProgressChanged(id: Int, bytesCurrent: Long, bytesTotal: Long) {
Log.i(tag, "bytesCurrent: $bytesCurrent, bytesTotal: $bytesTotal")
}
override fun onStateChanged(id: Int, state: TransferState?) {
if (state == TransferState.COMPLETED) {
e.onComplete()
}
}
override fun onError(id: Int, ex: java.lang.Exception) {
Log.d(tag, "error transfer s3: ${ex.message}", ex)
e.onError(ex)
}
})
}
});
}
completable2 is retrofit2 call:
#POST("some")
fun verifyLocation(#Header(AUTH_TOKEN_HEADER) authToken: String, #Body
verifyLocation: VerifyLocation): Completable

I'd guess transferObserver.setTransferListener calls the callback on the main thread which then will subscribe to completable2 on the main thread as well. You have to apply subscribeOn(Schedulers.io()) to completable2, just like in your other example.
val singleSubject = SingleSubject.create<String>();
completalbe1.subscribeOn(Schedulers.io())
.concatWith(completable2.subscribeOn(Schedulers.io())) // <-----------------------
.observeOn(Schedulers.io())
.subscribe({
singleSubject.onSuccess("ok")
}, { error ->
Log.e(tag, error.message, error)//here i got exception
singleSubject.onError(error)
})
return singleSubject
subscribeOn affects subscription (side) effects but your completalbe1 has an observation effect when it calls onComplete on the main thread.

Related

Amazon S3 buckets 403 error on some phone models

The problem is getting buckets on the S3 cloud. The most interesting thing here is that everything works fine on most phones, but not on all models and mainly on Samsung models (there are also Xiaomi and other models). Unfortunately, I'm not very familiar with the service. Is it possible to track requests in the service itself and understand why it gives a 403 error?
Here is the exception I am getting:
Non-fatal Exception: retrofit2.HttpException: HTTP 403 Forbidden
at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:53)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:129)
at okhttp3.RealCall$AsyncCall.run(RealCall.kt:140)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:923)
Here is the method to sign and send keys:
class AwsSignInterceptor(val signer: OkHttpAwsV4Signer) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
.addHeader("host", getHostHeader())
.addHeader("x-amz-date", getXAmzDateheader())
.addHeader("x-amz-content-sha256", "".sha256())
.addHeader("Range", "")
.build()
val signedRequest = signer.sign(request, S3_OBJECT_STORAGE_ACCESS_KEY, S3_OBJECT_STORAGE_SECRET_KEY)
FirebaseCrashlytics.getInstance().log(signedRequest.toString())
return chain.proceed(signedRequest)
}
And here is the method where this exception is thrown:
try {
api.getAlbum("$albumIdInt").downloadToFileWithProgress(albumIdInt)
.collect { download ->
when (download) {
is Download.Progress -> {
notifyWithProgress(download.percent)
}
is Download.Finished -> {
val zip = download.file
val result = mainInteractor.saveAlbum(albumIdInt.toString(), zip)
infoInteractor.update(albumIdInt.toString())
MainScope().launch {
if (result) {
notifyWithFinish()
} else {
notifyWithError()
FirebaseCrashlytics.getInstance().log("Error during saving file")
}
}
}
}
}
} catch (e: Exception) {
MainScope().launch {
Log.e("http", "${e.message}")
notifyWithError()
FirebaseCrashlytics.getInstance().recordException(e)
}
}

Kotlin coroutines crash with no helpful stacktrace

My Android app crashes and I see this stack trace in Logcat. It doesn't tell me which line of code is causing the problem.
2021-05-05 09:13:33.143 1069-1069/com.mycompany.app E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mycompany.app, PID: 1069
retrofit2.HttpException: HTTP 403
at retrofit2.KotlinExtensions$await$2$2.onResponse(KotlinExtensions.kt:53)
at retrofit2.OkHttpCall$1.onResponse(OkHttpCall.java:161)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Is there a way to map this back to my code, to see which call to retrofit is causing it? I have a repository with code like this:
suspend fun getSomeData(): Stuff {
return withContext(Dispatchers.IO) {
val body = myRetroApi.getStuff()
...
Do I need to wrap every withContext body to make sure no Throwables escape? I thought that if something threw an exception there, it would log an error, not crash the entire app.
Edit
I messed up when asking this question and put the emphasis on wrong things. So I'm removing the "retrofit" tag. It turns out the withContext(Dispatchers.IO) call does re-throw the Exception as expected, but when the exception gets back up to viewModelScope.launch, if that block does not catch it, the app crashes.
If the exception is not handled the app will crash of course.
You can add a try catch to avoid this:
suspend fun getSomeData() {
withContext(Dispatchers.IO) {
try{
val body = myRetroApi.getStuff()
...
} catch (e : Exception){
//your code
}
...
Retrofit is giving you a 403 Unauthorized HTTP exception. It may be that the server isn't passing any additional error message or that you need to catch HttpException and check for the message. In either case, this isn't a Retrofit issue hence it's just passing the error it's getting from the server you're calling.
It's best to create a network result wrapper and a wrapper function for API calls to handle exceptions.
You can do something like this. Keep in mind, the actual implementation is completely up to you. I would however suggest using runCatching when it comes to couroutines as it handles cancellation exceptions.
sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(val exception: Throwable, val message: String?) : NetworkResult<Nothing>()
}
suspend fun networkCall(): String = ""
suspend fun <T> safeApiCall(block: suspend () -> T): NetworkResult<T> {
return runCatching {
withContext(Dispatchers.IO) {
block()
}
}.fold({
NetworkResult.Success(it)
}, {
when (it) {
is HttpException -> NetworkResult.Error(it, "Network error")
else -> NetworkResult.Error(it, "Some other message...")
// else -> throw it
}
})
}
suspend fun getData() {
val result: NetworkResult<String> = safeApiCall {
networkCall()
}
when (result) {
is NetworkResult.Success -> {
//Handle success
}
is NetworkResult.Error -> { //Handle error
}
}
}
runCatching uses Kotlin's built-in Result class and there are several ways of handling the result. These are just a few.
runCatching {
//.....
}.getOrElse { throwable ->
//handle exception
}
runCatching {
//.....
}.getOrThrow()
runCatching {
}.onSuccess {
}.onFailure {
}

LiveDataReactiveStreams.fromPublisher crash application when not internet connection

W/System.err: java.net.UnknownHostException: Unable to resolve host "api.rawg.io": No address associated with hostname
W/System.err: at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:156)
at java.net.Inet6AddressImpl.lookupAllHostAddr(Inet6AddressImpl.java:103)
at java.net.InetAddress.getAllByName(InetAddress.java:1152)
at okhttp3.Dns$1.lookup(Dns.java:40)
at okhttp3.internal.connection.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:185)
at okhttp3.internal.connection.RouteSelector.nextProxy(RouteSelector.java:149)
at okhttp3.internal.connection.RouteSelector.next(RouteSelector.java:84)
at okhttp3.internal.connection.StreamAllocation.findConnection(StreamAllocation.java:214)
at okhttp3.internal.connection.StreamAllocation.findHealthyConnection(StreamAllocation.java:135)
at okhttp3.internal.connection.StreamAllocation.newStream(StreamAllocation.java:114)
at okhttp3.internal.connection.ConnectInterceptor.intercept(ConnectInterceptor.java:42)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.cache.CacheInterceptor.intercept(CacheInterceptor.java:93)
W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
at okhttp3.internal.http.BridgeInterceptor.intercept(BridgeInterceptor.java:93)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RetryAndFollowUpInterceptor.intercept(RetryAndFollowUpInterceptor.java:126)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
W/System.err: at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:254)
at okhttp3.RealCall.execute(RealCall.java:92)
at retrofit2.OkHttpCall.execute(OkHttpCall.java:186)
at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:45)
at io.reactivex.Observable.subscribe(Observable.java:10838)
at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:10838)
at io.reactivex.internal.operators.flowable.FlowableFromObservable.subscribeActual(FlowableFromObservable.java:29)
at io.reactivex.Flowable.subscribe(Flowable.java:12978)
at io.reactivex.internal.operators.flowable.FlowableOnBackpressureLatest.subscribeActual(FlowableOnBackpressureLatest.java:32)
at io.reactivex.Flowable.subscribe(Flowable.java:12978)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach.subscribeActual(FlowableDoOnEach.java:50)
at io.reactivex.Flowable.subscribe(Flowable.java:12978)
at io.reactivex.Flowable.subscribe(Flowable.java:12924)
at io.reactivex.internal.operators.flowable.FlowableSubscribeOn$SubscribeOnSubscriber.run(FlowableSubscribeOn.java:82)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
at java.util.concurrent.ThreadPoolExecutor.processTask(ThreadPoolExecutor.java:1187)
W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1152)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:929)
W/System.err: Caused by: android.system.GaiException: android_getaddrinfo failed: EAI_NODATA (No address associated with hostname)
W/System.err: at libcore.io.Linux.android_getaddrinfo(Native Method)
at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:74)
at libcore.io.BlockGuardOs.android_getaddrinfo(BlockGuardOs.java:200)
at libcore.io.ForwardingOs.android_getaddrinfo(ForwardingOs.java:74)
at java.net.Inet6AddressImpl.lookupHostByName(Inet6AddressImpl.java:135)
... 43 more
When application have connection with internet working good but when i have no internet connection my application crash. I want to handle this Exception but anything what i tried not helped :/ . I tried too check if source is not null . I am using Dagger 2 , Retrofit 2, RxJava, viewModel .
class VideoViewModel : ViewModel {
private val authApi: AuthApi
private val games: MediatorLiveData<TopGames> = MediatorLiveData<TopGames>()
#Inject
constructor(authApis: AuthApi) {
authApi = authApis
}
fun authenticateWithId(dates: String) {
val source: LiveData<TopGames> = LiveDataReactiveStreams.fromPublisher(
authApi.getTopGames(dates, "-added").doOnError { t-> print("${t.printStackTrace()} doOnError") }
.subscribeOn(Schedulers.io())
)
games.addSource<TopGames>(source, object : androidx.lifecycle.Observer<TopGames?> {
override fun onChanged(t: TopGames?) {
// Log.d("TAG", "VideoonChanged: $t")
games.value = t
games.removeSource(source)
}
})
}
fun observeGaneInfo(): LiveData<TopGames?>? {
return games
}
}
Other class
interface AuthApi {
#GET("/api/games")
fun getTopGames(
#Query("dates") dates: String,
#Query("ordering") ordering: String
): Flowable<TopGames>
}
There is no error handling in your rx chain. So you might to want to do something like this.
val source: LiveData<TopGames> = LiveDataReactiveStreams.fromPublisher(
authApi.getTopGames(dates, "-added")
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
games.value = it
},
{
// error handling goes here
}
)
)
You can have separate LiveData for your error messages, but the more cleaner approach would be to create State class, as mentioned in other replies, so you only have the single source of view state updates.
Also please note that the Disposable return from subscribe method is better to be disposed on ViewModel's onCleared method.
Solution :
create data class on State
data class DataWithStates<T>(
val data: T? = null,
val states: Throwable? = null
)
and in my live data i change val source: LiveData to LiveData<DataWithStates> and work perfectly
fun authenticateWithId(dates: String) {
val source: LiveData<DataWithStates<TopGames>> = LiveDataReactiveStreams.fromPublisher(
authApi.getTopGames(dates, "-added").map { lstUser -> DataWithStates(lstUser) }.onErrorReturn { ex -> DataWithStates(states = ex) }
.subscribeOn(Schedulers.io())
)
games.addSource<DataWithStates<TopGames>>(source, object : androidx.lifecycle.Observer<DataWithStates<TopGames>> {
override fun onChanged(t: DataWithStates<TopGames>) {
// Log.d("TAG", "VideoonChanged: $t")
games.value = t.data
games.removeSource(source)
}
})
}
helpful Link
RxJava to live data error Handling

Android Room RxJava UndeliverableException

I'm trying to fix some error reported by crashlytics in my app which I released to play store
I checked the log, but I don't know how to fix it...
io.reactivex.exceptions.UndeliverableException:
at io.reactivex.plugins.RxJavaPlugins.onError (RxJavaPlugins.java:367)
at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual (SingleFromCallable.java:50)
at io.reactivex.Single.subscribe (Single.java:3603)
at io.reactivex.internal.operators.maybe.MaybeFilterSingle.subscribeActual (MaybeFilterSingle.java:40)
at io.reactivex.Maybe.subscribe (Maybe.java:4290)
at io.reactivex.internal.operators.maybe.MaybeSubscribeOn$SubscribeTask.run (MaybeSubscribeOn.java:54)
at io.reactivex.Scheduler$DisposeTask.run (Scheduler.java:578)
at io.reactivex.internal.schedulers.ScheduledRunnable.run (ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call (ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run (FutureTask.java:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run (ScheduledThreadPoolExecutor.java:272)
at java.util.concurrent.ThreadPoolExecutor.runWorker (ThreadPoolExecutor.java:1133)
at java.util.concurrent.ThreadPoolExecutor$Worker.run (ThreadPoolExecutor.java:607)
at java.lang.Thread.run (Thread.java:762)
Caused by: android.arch.b.b.b:
at com.eastriver.workingtimer.data.source.WorkDao_Impl$4.call (WorkDao_Impl.java:172)
at com.eastriver.workingtimer.data.source.WorkDao_Impl$4.call (WorkDao_Impl.java:129)
at io.reactivex.internal.operators.single.SingleFromCallable.subscribeActual (SingleFromCallable.java:44)
at io.reactivex.Single.subscribe (Single.java:3603)
Actually, all my rx code is handling the error like this:
class MyIntentService(
private val disposable: CompositeDisposable = CompositeDisposable()
) : IntentService("MyIntentService") {
...
override fun onHandleIntent(intent: Intent?) {
disposable.add(
workDao.getWorkById(getToday())
.subscribeOn(Schedulers.io())
.subscribe({
// my logic
}, { t ->
Log.e(TAG, "error: ${t.message}", t)
})
)
}
override fun onDestroy() {
super.onDestroy()
disposable.clear()
}
}
And the WorkDao is:
#Dao
interface WorkDao {
#Query("SELECT * FROM work WHERE id = :id")
fun getWorkById(id: Long): Single<Work>
}
But I cannot find my error log in the crash report.
What should I do?
Ok, it looks to me that onDestroy is called before workDao.getWorkById(getToday()) is done. So it throws error while your subscriber is already disposed. What you can try now is handling errors from RxJava itself by following this link https://stackoverflow.com/a/49387916/2164363
Cheers

App falling down when Internet restored, RxJava & Retrofit

I need to get data from Api for each list element also I need to implement Internet connection checker.
Then program starts everything works great.
I disable Internet on live, and run method again (using swipeRefreshLayout), I receive InternetErrorToast, great!
I restored Internet on live, and run method again (using swipeRefreshLayout), app crashed with error.
Now I have following method.
Note! dataManager.getCityConditionsResponse() return Single
#Override
public void updateCitiesList() {
List<City> citiesList = dataManager.getCitiesFromDb();
if (!dataManager.isInternetConnected()) {
view.showCitiesList(citiesList);
view.showInternetErrorToast();
} else {
compositeDisposable.add(Observable.fromIterable(citiesList)
.doOnNext(city -> dataManager.getCityConditionsResponse(city.getQuery())
.subscribe(
response -> {
city.setTemp(response.getTemp());
city.setIcon(response.getIcon());
},
error -> view.showServerErrorToast()))
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
list -> view.showCitiesList(list),
error -> view.showServerErrorToast()
));
}
view.hideRefreshingStatus();
}
On step 3 I have this error
018-10-12 16:34:54.033 19013-19046/mike.weather E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-1
Process: mike.weather, PID: 19013
io.reactivex.exceptions.CompositeException: 2 exceptions occurred.
at io.reactivex.internal.observers.ConsumerSingleObserver.onError(ConsumerSingleObserver.java:49)
at io.reactivex.internal.operators.observable.ObservableSingleSingle$SingleElementObserver.onError(ObservableSingleSingle.java:93)
at retrofit2.adapter.rxjava2.BodyObservable$BodyObserver.onError(BodyObservable.java:72)
at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:56)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
at io.reactivex.Single.subscribe(Single.java:3438)
at io.reactivex.Single.subscribe(Single.java:3424)
at mike.weather.ui.main.MainActivityPresenter.lambda$updateCitiesList$3(MainActivityPresenter.java:49)
at mike.weather.ui.main.-$$Lambda$MainActivityPresenter$UqLPaAef0SB9PT-Rz654tgX3dnA.accept(Unknown Source:4)
at io.reactivex.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onNext(ObservableDoOnEach.java:93)
at io.reactivex.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onNext(ObservableDoOnEach.java:101)
at io.reactivex.internal.operators.observable.ObservableFromIterable$FromIterableDisposable.run(ObservableFromIterable.java:98)
at io.reactivex.internal.operators.observable.ObservableFromIterable.subscribeActual(ObservableFromIterable.java:58)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
at io.reactivex.Observable.subscribe(Observable.java:12090)
at io.reactivex.internal.operators.observable.ObservableToListSingle.subscribeActual(ObservableToListSingle.java:58)
at io.reactivex.Single.subscribe(Single.java:3438)
at io.reactivex.internal.operators.single.SingleDoOnSuccess.subscribeActual(SingleDoOnSuccess.java:35)
at io.reactivex.Single.subscribe(Single.java:3438)
at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:578)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
........................
It would be more Rx idiomatic if you rewrite your observable like that
compositeDisposable.add(Observable.fromArray(citiesList)
.flatMap(Observable::fromIterable)
.flatMapSingle(city ->
dataManager.getCityConditionsResponse(city.getQuery())
.map(response -> {
city.setTemp(response.getTemp());
city.setIcon(response.getIcon());
return city;
})
)
.toList()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
list -> view.showCitiesList(list),
error -> view.showServerErrorToast()
));
After that I think you problem will disappear.

Categories

Resources