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.
Related
Im currently developing an android app with Kotlin. Its my first experience with this programming language and im currently struggeling to translate an example of Java code to Kotlin.
I want to implement the answer to this question in Kotlin.
my current implementation fails to compile because the Ovservable::from method seems to be removed.
This is my current approach:
connectionObservable!!.flatMap { connection ->
connection.discoverServices()
.flatMap { services ->
services.getService(UUID.fromString("My UUID")).map(BluetoothGattService::getCharacteristics)
//here occurs the error, he wants a Single Source but got a observable with the ble characteristic
.flatMap { characteristics: MutableList<BluetoothGattCharacteristic> -> Observable.fromIterable(characteristics) }
.flatMap { characteristic: BluetoothGattCharacteristic ->
connection.setupNotification(characteristic)
.flatMap { observable: Observable<ByteArray> -> observable ,Pair<BluetoothGattCharacteristic, ByteArray>(characteristic, observable)}
}
}
}.subscribe { pair: Pair<BluetoothGattCharacteristic, ByteArray> ->
genericModel[pair.first.uuid] = pair.second
throwable -> { /* handle errors */ }
}
Can you point out my errors so i can understand what im doing wrong?
Thanks in advance!
Jonas
There are several potential issues with your code.
First of all the code is not syntactically correct — see this line:
.flatMap { observable: Observable<ByteArray> -> observable ,Pair<BluetoothGattCharacteristic, ByteArray>(characteristic, observable)}
I assume you probably wanted to flatMap the observable: Observable<ByteArray> (type added by me for clarity) to get from it ByteArray objects. This would look like this:
.flatMap { observable: Observable<ByteArray> -> observable.map { bytes -> Pair(characteristic, bytes) }}
Additionally the code will not compile as you try to return an Observable from a lambda which expects a SingleSource — exactly as your compiler says. If you have a Single and you will .flatMap it — it's lambda is supposed to return another SingleSource. There is a .flatMapObservable function that expects ObservableSource which is what you should use. The end result would look like:
connectionObservable!!.flatMap { connection ->
connection.discoverServices()
.flatMapObservable { services ->
services.getService(UUID.fromString("My UUID")).map(BluetoothGattService::getCharacteristics)
//here occurs the error, he wants a Single Source but got a observable with the ble characteristic
.flatMapObservable { characteristics: MutableList<BluetoothGattCharacteristic> -> Observable.fromIterable(characteristics) }
.flatMap { characteristic: BluetoothGattCharacteristic ->
connection.setupNotification(characteristic)
.flatMap { observable: Observable<ByteArray> -> observable.map { bytes -> Pair(characteristic, bytes) }}
}
}
}.subscribe { pair: Pair<BluetoothGattCharacteristic, ByteArray> ->
genericModel[pair.first.uuid] = pair.second
throwable -> { /* handle errors */ }
}
Observable.fromIterable() is still the API of Observable. You probably do not use the Observable class from proper package. There is an Observable class in package java.util but we are using here one from RxJava 2 which has package io.reactivex
I'm using RxJava with a retrofit to make API calls,
By using RxJava methods like flatMap and map I'm making API calls as well as performing DB operations in room database on the background thread.
My implementation is perfect and working fine if there is no error, but In the case when I got an error or any exception while performing DB Operation, Application getting crashed due to following Rx error.
E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-1
The exception was not handled due to missing onError handler in the subscribe() method call.
I have used RxJava to perform my operation as below :
mDataManager.login(params)
.flatMap { loginResponse: LoginResponse ->
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap { object: SomeDataModel ->
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
// here making another API call based on previos API
return#flatMap mDataManager...
}.map {
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
return#map true
}
.observeOn(mSchedulerProvider.ui())
.subscribeOn(mSchedulerProvider.io())
.subscribe({ result ->
// updating view
}, { throwable ->
throwable.printStackTrace()
})
I'm getting an exception while inserting data to DB
Exception java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase:
But the error not handled and Application getting crash.
The error says that missing onError handler in the subscribe() method but in my code, I already override throwable to handle exception/errors.
Can anyone find, what I'm missing or what mistake I have done in code.
UPDATE
Found the solution, Mistake was here :
mDataManager.insertDataToDB(object).subscribe()
In the Above statement, I'm subscribing but was not handling the error for that and because of that error was not handled by rxJava and in the result, the application gets crashed.
Final Code as below :
mDataManager.login(params)
.flatMap { loginResponse: LoginResponse ->
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap { object: SomeDataModel ->
// avoid this inner subscribe
// mDataManager.insertDataToDB(object).subscribe()
return#flatMap mDataManager.insertDataToDB(object)
}
.flatMap {
// here making another API call based on previos API result
return#flatMap mDatamanager....
}
.flatMap {
// avoid this inner subscribe
//mDataManager.insertDataToDB(object).subscribe()
return#flatMap mDataManager.insertDataToDB(object)
}
.observeOn(mSchedulerProvider.ui())
.subscribeOn(mSchedulerProvider.io())
.subscribe({ result ->
// updating view
}, { throwable ->
throwable.printStackTrace()
})
Above code is working Fine!
The reason is you are subscribing here
.map {
// here inserting data to DB
mDataManager.insertDataToDB(object).subscribe()
return#map true
}
And this subscribe is not handling the error scenario.
I feel it's not a good practice to call subscribe() for the inner streams. In your scenario the flow is broken in-between.
The best way according to me is instead of using map and calling subscribe() here, use,
flatMap{
object -> mDataManager.insertDataToDB(object)
}
This way, if any error, it will be caught in last outer subscribe().
Hope this answer helps.
I'm trying to handle errors in Rx android networking but unable to do so can some help.
getCompositeDisposable().add(getManager().callApi(request)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.ui())
.subscribe(apiResponse -> Log.d(TAG,"success"),
throwable -> Log.e(TAG,throwable.getMessage)));
I want to handle errors in subscribe()
Logs:
io.reactivex.exceptions.OnErrorNotImplementedException: java.net.ConnectException: Failed to connect
at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:704)
at io.reactivex.internal.functions.Functions$OnErrorMissingConsumer.accept(Functions.java:701)
at io.reactivex.internal.observers.ConsumerSingleObserver.onError(ConsumerSingleObserver.java:47)
at io.reactivex.internal.operators.single.SingleObserveOn$ObserveOnSingleObserver.run(SingleObserveOn.java:79)
at io.reactivex.android.schedulers.HandlerScheduler$ScheduledRunnable.run(HandlerScheduler.java:109)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7231)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
Caused by: com.androidnetworking.error.ANError: java.net.ConnectException: Failed to connect to /192.168.1.42:8484
at com.rx2androidnetworking.Rx2InternalNetworking$SimpleANObservable.subscribeActual(Rx2InternalNetworking.java:235)
at io.reactivex.Observable.subscribe(Observable.java:10981)
at io.reactivex.internal.operators.observable.ObservableSingleSingle.subscribeActual(ObservableSingleSingle.java:35)
at io.reactivex.Single.subscribe(Single.java:2801)
at io.reactivex.internal.operators.single.SingleSubscribeOn$SubscribeOnObserver.run(SingleSubscribeOn.java:89)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
Unfortunately i can not comment. Could you be more specific? What error have you got? The common way to handle errors using rx looks like this:
Subscription subscribe = apiServices.endpointCall(...parameters...)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io());
.subscribe(response -> {
//do something with response
}, throwable -> {
//do something with error
});
Where apiService is created by retrofit using
service = retrofit.create(ApiServices.class);
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.
I am getting a lot of NPE crashes on Crashlytics that are related with an rx-wrapper I've created for FirebaseRemoteConfig.
My code:
public Completable fetch() {
remoteConfig.activateFetched();
return Completable.create(e ->
remoteConfig.fetch(cacheExpiration).addOnCompleteListener(
task -> {
if (task.isSuccessful()) {
Log.d(TAG, "Config fetched successfully");
remoteConfig.activateFetched();
if (!e.isDisposed()) {
e.onComplete();
}
} else {
if (!e.isDisposed()) {
Log.e(TAG, "Config fetch error", task.getException());
e.onError(task.getException());
}
}
}));
}
And then I subscribe to it like this:
composite.clear();
composite.add(
helper.fetch()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableCompletableObserver() {
#Override
public void onComplete() {
Timber.tag(TAG).d("Remote config fetched");
}
#Override
public void onError(#NonNull Throwable throwable) {
Timber.tag(TAG).d(throwable, "Remote config fetch error");
}
})
);
I get 3 types of crashes:
Crash #1
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableCreate.subscribeActual(CompletableCreate.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableSubscribeOn$SubscribeOnObserver.run(CompletableSubscribeOn.java:64)
at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452)
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:237)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:152)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
at java.lang.Thread.run(Thread.java:841)
Crash #2
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableSubscribeOn.subscribeActual(CompletableSubscribeOn.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableObserveOn.subscribeActual(CompletableObserveOn.java:34)
at io.reactivex.Completable.subscribe(Completable.java:
Crash #3
Fatal Exception: java.lang.NullPointerException
at io.reactivex.internal.operators.completable.CompletableObserveOn$ObserveOnCompletableObserver.onSubscribe(CompletableObserveOn.java:68)
at io.reactivex.internal.operators.completable.CompletableSubscribeOn.subscribeActual(CompletableSubscribeOn.java:36)
at io.reactivex.Completable.subscribe(Completable.java:1635)
at io.reactivex.internal.operators.completable.CompletableObserveOn.subscribeActual(CompletableObserveOn.java:34)
at io.reactivex.Completable.subscribe(Completable.java:1635)
The curious thing is that the crashes only happen on Android 4, mainly 4.4 and 4.1.
The thing all these errors have in common is that they appear to be caused by a null where a CompletableObserver is expected.
Given that the subscribeWith gets a valid object the only way for this to occur is through a misconfiguration of the RxJavaPlugin.onSubscribe hook, which can wrap observers and thus for some reason replace the observer with a null.
I suggest inspecting the onCompletableSubscribe field in the RxJavaPlugins class using reflection to see if there is some callback that might be acting weirdly on those java versions.