RxJava doOnError vs onError - android

I am try to use the following code
initLocalSettingsIfNeed()
.andThen(initGlobalSettingsIfNeed(configuration))
.doOnComplete(callback::onSuccess)
.doOnError(throwable -> callback.onError(throwable.getLocalizedMessage()))
.subscribe();
But I have exception
The exception was not handled due to missing onError handler in the
subscribe() method call.
I guess I am not using this methods correctly, I thought can replace doOnComplete doOnError with observer inside subscribe() method, I am wrong?

Regarding your original question, you have to know that doOnError is not a replacement of onError. You have a good and short explanation about it in this blog:
Actually there’s one key difference between them. doOnError() basically only triggers its callback, then passes down the encountered errors to the down stream. So if the whole stream is subscribed without the onError callback in subscribe(), your app will crash by OnErrorNotImplementedException.
The onError callback in subscribe() in the other hand does consume the
errors. That means, it will catch the errors, and let you handle them
without re-throwing the errors by itself.
About the warning you mention in one comment:
This approach is working, but i have warning 'the result of subscribe
not used', as i know this need to be disposed automatically when
onError or onComplete is called, is there way to avoid this warning? – Pavel Poley
A good approach is that your methods inside your Repository return a Observable, and then you can subscribe to them in your ViewModel. Then, in every ViewModel class you can have a member variable with a CompositeDisposable where you can add the disposable of each subscription to the Observables returned by your repository. Finally, you should override the onCleared method to dispose all the disposables stored in the CompositeDisposable.
public class MyViewModel extends ViewModel {
private MyRepository myRepository;
private final CompositeDisposable disposables;
#Inject
public MyViewModel(MyRepository myRepository) {
...
this.myRepository = myRepository;
disposables = new CompositeDisposable();
...
}
public void callObservableInRepository() {
disposables.add(myRepository.myObservable()
.subscribe(onSuccess -> {...} , onError -> {...}));
}
#Override
protected void onCleared() {
disposables.clear();
}
}

try this
initLocalSettingsIfNeed()
.andThen(initGlobalSettingsIfNeed(configuration))
.subscribe({completed->
callback.onSuccess()
},{throwable->
callback.onError(throwable.getLocalizedMessage())
})

Related

Suscribing to observable: RxJava + Retrofit + Repository pattern

I am trying to set up retrofit and rxjava for my app to make api calls to a webservice. I am having some trouble subscribing to my observable object in the main activity. The architecture follows the viewmodel/repository pattern, here is my code
Repository
class WisproRepository() {
val request = ServiceBuilder.buildService(JsonPayments::class.java)
val apicall: Observable<Payment> = request.getPostsV2(2, 100, "authorizationcode")
fun getPayments(): Observable<Payment?>? {
return apicall.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
}
}
ViewModel
class PaymentsViewModel : ViewModel() {
var wisproRepository = WisproRepository()
var payments_rx: Observable<Payment?>? = wisproRepository.getTopContributors()
}
MainActivity
class MainActivity : AppCompatActivity() {
var adapter: MyRecyclerViewAdapter? = null
private var textView: TextView? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Getting payment object and updating view
val view_model: PaymentsViewModel by viewModels()
view_model.payments_rx?.subscribe(....)
}
Now, instead of observing the livedata I would have to subscribe to the Observable and setup recyclerview when the data arrives but I don't understand how to do that in the subscribe method. But I don't understand how to do that inside the subscribe method. Also, how can I access the data of the Payment inside the observable object ?
Also I do not understand these lines right here,
val request = ServiceBuilder.buildService(JsonPayments::class.java)
val apicall: Observable<Payment> = request.getPostsV2(2,100, "authorization code")
apicall.subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread())
Are subscribeOn and observeOn performing the call? And if they are, how does it work (I am used to enqueue method)? Am I missing something? How can I setup onResponse and onFailure callbacks?
If you use retrofit2:adapter-rxjava2 it automatically wrap your API call to Cold Observable and auto-execute API Request when you subscribe.
Are subscribeOn and observeOn performing the call
It doesn't perform call API, API call when you call subscribe it makes your API request run in Background Thread and handles response in Main Thread
How can I setup onResponse and onFailure callbacks?
In subscribe method you can pass Consumer to handle Response or Error
view_model.payments_rx?.subscribe({
// response
}, {
// error
})
An observable is basically a stream of events, each event is of the type that you're observing. In this case Payment. Every time you get a Payment object, the onNext() method of your observable is called. Once the stream is done emitting events onComplete will be called, and in case of an error, onError (both of these are terminal events)
You haven't posted the JsonPayments class so I can't say for sure, but you need to be creating an observable in getPostsV2() and returning it so you can observe it or subscribe to it.
If you want onResponse and onFailure callbacks, you could essentially wrap your Payment object to Result<Payment> where Result has both of those properties. Your final observable will be Observable<Result<Payment>> where each event will be of type Result<Payment>.

Creating a RX Observable without calling onComplete is it Valid?

I am not expert in Rx sorry if it is trivial question so.I am doing a polling operation which I have to wait for updates and I created Rx Observable for that; however, I never call onComplete. but in onDestroy I unsubscribe. please take a look on the below code.
#Reusable
class PollingExample #Inject constructor() {
var itemObservable: Observable<List<Item>>
private set
private lateinit var itemObservableEmitter: WeakReference<ObservableEmitter<List<Item>>>
init {
itemObservable = Observable.create { e -> itemObservableEmitter = WeakReference(e) }
}
fun submitData(items: List<Item>) {
itemObservableEmitter.get()?.onNext(items)
}
}
is it Valid to do something like that
After some search I think as long as I do not want to call onError() or onComplete() it is better to use this library https://github.com/JakeWharton/RxRelay which guarantee that everything will continue working without the worry of accidentally triggering a terminal state

Rx disposable not being created on time in unit tests

I'm having trouble with testing the manual manipulation of disposables inside my class being tested. I have a itemsProcessed map that keeps track of the different disposables that are created, the reason is that some disposables I need to be able to dispose on demand, and others I just need to know they exist.
My class runs fine and everything works as expected, but in my tests, I noticed that the disposables that I make aren't being inserted into my map until what would be the async code is already completed.
I'm not sure if it has anything to do with the fact that I'm using
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
RxAndroidPlugins.setInitMainThreadSchedulerHandler { Schedulers.trampoline() }
Here are the relevant methods I'm testing, the myRepo.processItem() call is on a background thread
private fun processItem(item: Item) {
itemsProcessed[item] = myRepo.processItem(item)
.doOnComplete {
safelyDelete(item)
itemsProcessed.remove(item)
}
.doOnError {
itemsProcessed.remove(item)
}
.subscribe({}, {})
}
private fun cleanOldItems() {
itemList.forEach {
if (!itemsProcessed[item].exist())
safelyDelete(it)
}
}
myRepo has a processor called itemProcessor which calls the method above, my test is as follows
#Test
fun doNotDeleteItemsBeingProcessed() {
`when`(itemProcessor.processItem(any()))
.thenAnswer {
//from my understanding of disposables, the disposable should have been made in my real class and should have been inserted into the map at this point
trigger cleanOldItems
Completable.timer(5000, TimeUnit.MILLISECONDS)
}
repo.triggerProcessItems()
Assert.assertTrue(itemList.contains(item))
}
It seems like when I run the test, itemsProcessed map in my class is empty until the last assert line in my test is reached. When I added in doOnSubscribe, I noticed that doOnSubscribe was also called at the very end, what's causing this behaviour?
What does trigger cleanOldItems do? Assuming this is a synchronous call, then it'll call your cleaning function before it returns any disposable to store in your map. Instead, you should have
#Test
fun doNotDeleteItemsBeingProcessed() {
`when`(itemProcessor.processItem(any()))
.thenAnswer {
//from my understanding of disposables, the disposable should have been made in my real class and should have been inserted into the map at this point
Completable.timer(5000, TimeUnit.MILLISECONDS)
}
repo.triggerProcessItems()
repo.triggerCleanOldItems()
Assert.assertTrue(itemList.contains(item))
}
Also bear in mind that Completable.timer call uses computation scheduler internally, so unless you explicitly provide a scheduler to it (as in Completable.timer(5000, TimeUnit.MILLISECONDS, Schedulers.trampoline()) or override setComputationSchedulerHandler your subscription will be triggered on a different thread.

RxJava2 Letting network call complete if fragment/activity changes

I have a series of sequential Parse network calls that are all dependent on each other to save the final object all wrapped by an Observable so I can deal with everything as one call in my presenter.
If the user decides to change fragments, or leave the app or whatever, this network call is important enough that I'd like it to attempt to complete.
If I call disposables.dispose() the observable will throw an error (which I can catch) on the next .save method in the Observable and the network call does not finish.
If I don't dispose of it, the network call finishes, but it will call my onComplete and throw an error since the view is gone. I can stop that error from happening, but then I'm worried that I've created a memory leak.
I don't care if the oncomplete/onerror get called if the user gets into this situation but I would like to ensure it completes one way or another.
Is there any way to let the call complete, but not cause a memory leak by not disposing it?
fun doParseNetworkCall(){
return Observable.create<Boolean> { emitter ->
createParseObject1
createParseObject1.save()
createParseObject2
createParseObject2.add(key, createParseObject1)
createParseObject2.save()
createParseObject3
createParseObject3.add(key, createParseObject2)
createParseObject3.save()
emitter.onNext(true)
}
fun doNetworkCall(){
repo.doParseNetworkCall()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribeBy(
onError = { error ->
//do something
},
onComplete = {
//do something
}
).addTo(disposable)
}
I was able to resolve my issue doing the following. I think it's memory safe since the 2nd set of observables don't subscribe until the first observable completes, and if the Composite Disposable in the presenter has been disposed of already, the 2nd set will not subscribe.
repo.saveSomething()
.map {
//Do some non view stuff
}
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribeBy(
onError = { error ->
onErrorMethod()
},
onComplete = {
onSuccessMethod()
}
)
fun onSuccessMethod() {
Observable.just(true)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribeBy(
onComplete = {
//Do view stuff
}).addTo(disposable)
}
fun onErrorMethod() {
Observable.just(true)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io()).subscribeBy(
onComplete = {
//Do view stuff
}).addTo(disposable)
}

The result of subscribe is not used

I've upgraded to Android Studio 3.1 today, which seems to have added a few more lint checks. One of these lint checks is for one-shot RxJava2 subscribe() calls that are not stored in a variable. For example, getting a list of all players from my Room database:
Single.just(db)
.subscribeOn(Schedulers.io())
.subscribe(db -> db.playerDao().getAll());
Results in a big yellow block and this tooltip:
The result of subscribe is not used
What is the best practice for one-shot Rx calls like this? Should I keep hold of the Disposable and dispose() on complete? Or should I just #SuppressLint and move on?
This only seems to affect RxJava2 (io.reactivex), RxJava (rx) does not have this lint.
The IDE does not know what potential effects your subscription can have when it's not disposed, so it treats it as potentially unsafe. For example, your Single may contain a network call, which could cause a memory leak if your Activity is abandoned during its execution.
A convenient way to manage a large amount of Disposables is to use a CompositeDisposable; just create a new CompositeDisposable instance variable in your enclosing class, then add all your Disposables to the CompositeDisposable (with RxKotlin you can just append addTo(compositeDisposable) to all of your Disposables). Finally, when you're done with your instance, call compositeDisposable.dispose().
This will get rid of the lint warnings, and ensure your Disposables are managed properly.
In this case, the code would look like:
CompositeDisposable compositeDisposable = new CompositeDisposable();
Disposable disposable = Single.just(db)
.subscribeOn(Schedulers.io())
.subscribe(db -> db.get(1)));
compositeDisposable.add(disposable); //IDE is satisfied that the Disposable is being managed.
disposable.addTo(compositeDisposable); //Alternatively, use this RxKotlin extension function.
compositeDisposable.dispose(); //Placed wherever we'd like to dispose our Disposables (i.e. in onDestroy()).
The moment the Activity will be destroyed, the list of Disposables gets cleared and we’re good.
io.reactivex.disposables.CompositeDisposable mDisposable;
mDisposable = new CompositeDisposable();
mDisposable.add(
Single.just(db)
.subscribeOn(Schedulers.io())
.subscribe(db -> db.get(1)));
mDisposable.dispose(); // dispose wherever is required
You can subscribe with DisposableSingleObserver:
Single.just(db)
.subscribeOn(Schedulers.io())
.subscribe(new DisposableSingleObserver<Object>() {
#Override
public void onSuccess(Object obj) {
// work with the resulting todos...
dispose();
}
#Override
public void onError(Throwable e) {
// handle the error case...
dispose();
}});
In case you need to directly dispose Single object (e.g. before it emits) you can implement method onSubscribe(Disposable d) to get and use the Disposable reference.
You can also realize SingleObserver interface by your own or use other child classes.
As was suggested you may use some global CompositeDisposable to add the result of the subscribe operation there.
The RxJava2Extensions library contains useful methods to automatically remove created disposable from the CompositeDisposable when it completes. See subscribeAutoDispose section.
In your case it may look like this
SingleConsumers.subscribeAutoDispose(
Single.just(db)
.subscribeOn(Schedulers.io()),
composite,
db -> db.playerDao().getAll())
You can use Uber AutoDispose and rxjava .as
Single.just(db)
.subscribeOn(Schedulers.io())
.as(AutoDispose.autoDisposable(AndroidLifecycleScopeProvider.from(this)))
.subscribe(db -> db.playerDao().getAll());
Make sure that you understand when you unsubscribe based on the ScopeProvider.
Again and again I find myself coming back to the question of how to correctly dispose of subscriptions, and to this posting in particular. Several blogs and talks claim that failing to call dispose necessarily leads to a memory leak, which I think is a too general statement. In my understanding, the lint warning about not storing the result of subscribe is a non-issue in some cases, because:
Not all observables run in the context of an Android activity
The observable can be synchronous
Dispose is called implicitly, provided the observable completes
Since I don't want to suppress lint warnings I recently started to use the following pattern for cases with a synchronous observable:
var disposable: Disposable? = null
disposable = Observable
.just(/* Whatever */)
.anyOperator()
.anyOtherOperator()
.subscribe(
{ /* onSuccess */ },
{ /* onError */ },
{
// onComplete
// Make lint happy. It's already disposed because the stream completed.
disposable?.dispose()
}
)
I'd be interested in any comments on this, regardless of whether it's a confirmation of correctness or the discovery of a loophole.
There's another way available, which is avoiding to use Disposables manually (add and remove subscriptions).
You can define an Observable and that observable is going to receive the content from a SubjectBehaviour (in case you use RxJava). And by passing that observable to your LiveData, that should work. Check out the next example based on the initial question:
private val playerSubject: Subject<Player> = BehaviorSubject.create()
private fun getPlayer(idPlayer: String) {
playerSubject.onNext(idPlayer)
}
private val playerSuccessful: Observable<DataResult<Player>> = playerSubject
.flatMap { playerId ->
playerRepository.getPlayer(playerId).toObservable()
}
.share()
val playerFound: LiveData<Player>
get() = playerSuccessful
.filterAndMapDataSuccess()
.toLiveData()
val playerNotFound: LiveData<Unit>
get() = playerSuccessful.filterAndMapDataFailure()
.map { Unit }
.toLiveData()
// These are a couple of helpful extensions
fun <T> Observable<DataResult<T>>.filterAndMapDataSuccess(): Observable<T> =
filter { it is DataResult.Success }.map { (it as DataResult.Success).data }
fun <T> Observable<DataResult<T>>.filterAndMapDataFailure(): Observable<DataResult.Failure<T>> =
filter { it is DataResult.Failure }.map { it as DataResult.Failure<T> }
If you are sure that disposable handled correctly, for example using doOnSubscribe() operator, you may add this to Gradle:
android {
lintOptions {
disable 'CheckResult'
}}

Categories

Resources