How can I subscribe and unsubscribe using onResume() and onPause() methods with RxJava 2? - rx-android

Well, I need to bind emitting to my activity's lifecycle. How can I do that? And when should I create observer and observable instances?

If you have an observable that you want data from sometimes and not at other times, there is a simple way to subscribe and unsubscribe using the switchMap() operator.
Let's say you have an observable that want data from:
Observable<LocationData> locationDataObservable;
Then, if you introduce a switching observable:
PublishSubject<Boolean> switchObservable = PublishSubject.create();
you can control the subscriptions to the first observable:
Observable<LocationData> switchedLocationDataObservable =
switchObservable
.switchMap( abled -> abled ? locationDataObservable : Observable.never() )
.subscribe();
To enable receiving the data, perform
switchObservable.onNext( Boolean.TRUE );
and to disable,
switchObservable.onNext( Boolean.FALSE );
The switchMap() operator takes care of subscribing and unsubscribing for you.

It can be solved without Observable.never(). Just use BehaviourSubject with takeUntil and repeatWhen operators.
private fun getPauseUpdatesSource(): Flowable<Unit> {
return setUpdatesPausedSubject
.filter { it }
.map { }
.toFlowableLatest()
}
private fun getResumeUpdatesSource(): Flowable<Unit> {
return setUpdatesPausedSubject
.filter { it.not() }
.map { }
.toFlowableLatest()
}
And in the rx chain:
locationDataObservable
.takeUntil(getPauseUpdatesSource())
.repeatWhen { it.switchMap { getResumeUpdatesSource() } }
It just pauses your chain and doesn't emit any empty observables

Related

How Can I use the Bind Method in RXSwift?

The following sample code throws an error stating
No exact matches in call to instance method 'bind'
How can I bind the onNext of my publish subject to my observable?
let publish = PublishSubject<Void>()
// Error Here
publish.bind(to: myMethod())
func myMethod() -> Observable<Void> {
return Observable.create{ observer in
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
}
So what I want is everytime my publish subject emits an onNext event I want to trigger the observable of 'myMethod'
I'm not sure how to interpret this, but it sounds like you want something like:
let publish = PublishSubject<Void>()
let response = publish.flatMap { myMethod() }
response
.bind(onNext: { print($0) })
func myMethod() -> Observable<Void> {
Observable.create{ observer in
observer.onNext(())
observer.onCompleted()
return Disposables.create()
}
}
But it all seems rather pointless since all myMethod() does is emit a next event.
First, the bind(to:) method is in the RxCocoa Framework so you need to add import RxCocoa in your file.
Then, the observable created by myMethod will only be able to emit the event from the .create block. You can't use bind on it. If you need an observable with both the events from your subject and from the myMethod observable, you can do that :
let thirdObservable = Observable.merge(publish, myMethod())

is there a better way to code for two dependent observables

In the method below I want to call two observables. After the data from first observable (getUnrecoveredGearsExt- a http req) is returned I want to pass the data to the second observable (createUpdate- persist to indexDB). Is there a cleaner way to achieve this maybe using some of the rxjs operators. thanks
Note: after the successful completion of the second observable I want to return the data from the first Observable. The use case is get data from the backend and store locally in indexDB and if successful return data or error
public getAndUpdateUnrecoveredGears(cfr: string, maxResults?: number, excludeTripid?: string) : Observable<GearSet[]> {
return Observable.create((observer) => {
this.getUnrecoveredGearsExt(cfr,maxResults,excludeTripid).subscribe(
(gears: GearSet[]) => {
this.createUpdate(gears).subscribe(
() => {
observer.next(gears);
observer.complete();
},
(error) => {
observer.error(error);
}
);
},
(error) => {
observer.error(error);
}
);
});
}
Having nested .subscribe() methods is an anti-pattern of RxJS and can cause many issues. So it's a strong signal of when you need to use operators. Fortunately, there is one which simplifies your code.
public getAndUpdateUnrecoveredGears(cfr: string, maxResults?: number, excludeTripid?: string) : Observable<GearSet[]> {
return this.getUnrecoveredGearsExt(cfr,maxResults,excludeTripid).pipe(
concatMap((gears:GearSet[])=>this.createUpdate(gears))
);
}
Because we're dealing with HTTP requests, they'll emit one value then complete. For this, we can use concatMap(). This operator will wait until getUnrecoveredGearsExt() completes, and then will subscribe to createUpdate() using the value emitted from getUnrecoveredGearsExt(). The operator will then emit any values coming from this "inner observable".
Assuming createUpdate() is also an HTTP request, it will automatically send a complete signal after emitting the response.
The solution below works for me. The final issue was how to pass the previous result 'gears' out as a final result. This is achieved by using the combineLatest to pass the two results to the next map operator, which can then pass gears out.
public getAndUpdateUnrecoveredGearsAlt2(cfr: string, maxResults?: number, excludeTripid?: string): Observable<GearSet[]> {
return this.getUnrecoveredGearsExt(cfr,maxResults,excludeTripid).pipe(
switchMap((gears: GearSet[]) => { return combineLatest(this.createUpdate(gears),of(gears));}),
map(([temp, gears]) => gears )
);
}

RxJS Unsubscribe Only From Inner Observable

Let's say I have an interval that each second sends an heartbeat. At each beat i'd like to inspect something on my web page and react accordingly. I'd also like the option to unsubscribe from the inner Observables actions, but keep getting the heartbeat so when i subscribe back, everything will flow as before.
Creating a Subscription from Interval and piping it leaves no option to unsubscribe from the inner action, but only the whole subscription as whole.
Is there a way to return the inner Observable so i can unsubscribe from it while still retaining the heartbeat created from the Interval?
Edit: I've tried to create a class to describe what I'm talking about:
class Monitor {
sub: Subscription | null = null;
start() {
this.sub = this.monitor().subscribe();
}
monitor() {
const dom$ = someSelectorObserver(this.win.document, '#someSelector').pipe(
mergeMap(newElementOrBail => {
if (newElementOrBail) {
return handle(newElementOrBail);
} else {
return bail();
}
}),
tap({
error: error => this.log.error(error),
}),
);
return dom$;
}
handle(ele: HTMLElement) {
// do stuff
}
bail() {
this.sub.unsubscribe();
}
}
So basically my monitor starts with creating the subscription, as long as there's a new element to handle everything is fine, but when a bail signal appears I'd like to unsubscribe while still monitoring the DOM changes for a return of the previous elements.
So the outer subscription is basically the DOM observer and the inner is the mergeMap handle function. Does it make more sense?
You could just put some conditional on your inner observable:
private takeSignal = true
interval(3000).pipe(switchMap(() => takeSignal ? inner$ : NEVER))
Then just flip takeSignal as needed.
But it seems easier to just unsubscribe from the whole thing and resubscribe when needed. Why keep the interval going when you’re not using it?
You can split your logic in two (or more) streams.
Store heartbeat$ in a separate variable and subscribe to multiple times for different reasons.
In this way, you'd be able to split your logic into different streams and control subscriptions individually.
const heartbeat$ = interval(3000);
const inspectWeb = heartbeat$.pipe(
// do stuff
).subscribe()
inspectWeb.unsubscribe()
heartbeat$.pipe(
// do other stuff
).subscribe()

BehaviorSubject firing twice

Im using BehaviourSubject from RxJS:
private rights = new BehaviorSubject<Array<string>>([]);
updateRights(rights: Array<string>) {
this.rights.next(rights);
}
getRights(): Observable<any> {
return this.rights.asObservable();
}
I'm updating the rights in the root component and im subscribing to it in another component like:
this.configService.getRights().subscribe(res => {
console.log(res);
})
This subscription is firing twice. Once when the data is empty and then again when the data is received.
I want the subscription to fire only once and get only the latest data. What should be done?
BehaviourSubject emits the value on subscription by default, and it is intended design. If you do not want this behaviour, use Subject instead.
Do it this way:
private currnetRightsSubject: BehaviorSubject<string[]>;
public currentRights: Observable<string[]>;
constructor() {
this.currnetRightsSubject= new BehaviorSubject<string[]>(/*.......*/);
this.currentRights= this.currnetRightsSubject.asObservable();
}
public get currentRightsValue(){
return this.currnetRightsSubject.value;
}
updated
fill the BehaviorSubject like this:
this.currnetRightsSubject.next(someValue);

How could you specify an API that returned Observables?

An Observable - a collection over time - is a useful thing to be able to request over the web. A feed is best modeled as an Observable, not a static array that you must poll and diff to request.
My question - if I wanted to create a web endpoint that would let you do
web-tail -f http://somewhere.com/biz-quotes
This service, queried by a fictional utility web-tail, would every 5 seconds a new pithy business quote like "Custom departmental synergy" would be returned. I could write such a web-tail utility with WebSockets, and establish a convention for what field of emitted objects would be emitted to the console. But what language would I write a consumable specification in?
Is the Observable specification mature enough to be referenced?
If your goal is to write a client which consumes messages sent by a server over websockets, you can definitely use RxJs on top of, say, socket.io.
This nice article explains you how this can work.
In a nutshell this is the TypeScript code you need.
import { Observable } from 'rxjs';
import { Subject } from 'rxjs';
import { Observer } from 'rxjs';
import * as socketIoClient from 'socket.io-client';
export class SocketObs {
private socket: SocketIOClient.Socket;
private connect = new Subject<any>();
private disconnect = new Subject<any>();
constructor(url: string);
constructor(input: any) {
this.socket = socketIoClient(input);
this.socket.on('connect',
() => {
this.connect.next();
// complete to make sure that this event is fired only once
this.connect.complete();
}
);
this.socket.on('disconnect',
() => {
this.disconnect.next();
// complete to make sure that this event is fired only once
this.disconnect.complete();
}
);
}
send(event, message?) {
this.socket.emit(event, message);
}
onEvent(event): Observable<any> {
return new Observable<any>((observer: Observer<any>) => {
this.socket.on(event, data => observer.next(data));
});
}
onDisconnect() {
return this.disconnect.asObservable();
}
onConnect() {
return this.connect.asObservable();
}
close() {
this.socket.close();
}
}
SocketObs class offers you the API you need in form of Observable, in particular onEvent returns an Observable which emits any time a certain event is received from the server.

Resources