I'm new to Android development and am currently trying to make a simple MVC app that works with Rest API.
API calls are made without using Retrofit, although this is not so important. The main catch is that using Observable with debounce and SwitchMap I still get too many API calls (and the extra ones should be discarded). The function is called when text is entered (EditText Listener with TextWatcher). And when administered continuously without delay word, every symbol processed by the server and should only be administered when not within 600 milliseconds. Please help me.
public Observable<String> getObservable(final String s){
return Observable
.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext(model.translateText(s));
}
});
}
public Observer<String> observer = new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String s) {
mainView.hideProgress();
mainView.showResult(s);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
};
public void onEditTextChange(String textForTranslate){
mainView.showProgress();
getObservable(textForTranslate)
.debounce(600,TimeUnit.MILLISECONDS)
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
}
You are creating a new observable every time a character is typed. So multiple observers are created with each having separate debounce (time throttle) and switch but they are not reused. Instead you create a new observable whenever text changes and start rx chain on it.
You need to create a single PublishSubject
private final PublishSubject<String> querySubject = PublishSubject.create();
that emits entered text/query whenever text is changed. Use it in your callback:
public void onEditTextChange(String textForTranslate) {
querySubject.onNext(textForTranslate);
}
And in your main function, subscribe to observable:
querySubject
.debounce(600, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.switchMap(new Function<String, ObservableSource<String>>() {
#Override
public ObservableSource<String> apply(String s) throws Exception {
// perform api call or any other operation here
return Observable.just(s);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(observer);
Debounce operator emits single item only after given time (600 ms) has passed. It ignores items if current item is being processed and given time has not passed.
distinctUntilChanged helps in reducing processing of same query.
Related
I'm new to RxJava, I know flatmaps are for mapping an emitted item to observable. I also know that based on the documentation the emitted observables all get combined (flatten) to a single observable stream.
I was wondering what happens if any of those inner observables get completed?
for example: I have an observable that emits a item data key. I have to make another async http call to get the item data from the server, so I call it by using another observable. I use a flat map to connect these two and create one main observable.
When does the run() method of following "SomeMethodThatWantsItems" get called?
public void someMethodThatWantsItems(MyHttpCaller httpCaller, SomeSearchEngine searchEngine)
{
Consumer<Item> onNextConsumer =
Observable<Item> searchObservable = getSearchResult(httpCaller, searchEngine, "The Search Word");
searchObservable
.subscribeOn(Schedulers.newThread())
.subscribe(new Consumer<Item>(){
#Override
public void accept(#NonNull Item item) throws Exception {
//Do stuff with the item
}
}
, new Consumer<Exception>() { //some implementation of onErrorConsumer
}
//OnComplete
, new Action(){
#Override
public void run() throws Exception {
//When does this get called??? after the search complete or when the first http call is successful?
}
});
}
private Observable<String> getSearchResultKeys(SomeSearchEngine searchEngine, String someSearchWord)
{
return Observable.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(#NonNull final ObservableEmitter<String> emitter) throws Exception {
//Assume that our search engine call onFind everytime it finds something
searchEngine.addSearchListener(new searchEngineResultListener(){
#Override
public void onFind(String foundItemKey){
emitter.onNext(foundItemKey);
}
#Override
public void onFinishedFindingResults(){
emitter.onComplete();
}
});
}
});
}
private Observable<Item> getItemByKey(MyHttpCaller httpCaller, String key)
{
return Observable.create(new ObservableOnSubscribe<Item>() {
#Override
public void subscribe(#NonNull final ObservableEmitter<Item> emitter) throws Exception {
//Call the server to get the item
httpCaller.call(key, new onCompleteListener(){
#Override
public void onCompletedCall(Item result)
{
emitter.onNext(result);
//The result is complete! end the stream
emitter.onComplete();
}
});
}
});
}
public Observable<Item> getSearchResult(MyHttpCaller httpCaller, SomeSearchEngine searchEngine, String someSearchWord){
//Where everything comes together
Observable<String> searchResultObservable = getSearchResultKeys(searchEngine, someSearchWord);
retuern searchResultObservable
.observeOn(Schedulers.newThread())
.flatMap(new Function<String, Observable<Item>>(){
#Override
public Observable<Item> apply(String key){
return getItemByKey(httpCaller, key);
}
});
}
The onComplete() always get call once and then the streams stops. (this is part of the Observable Contract).
That means that in your case, your onComplete() at SomeMethodThatWantsItems will be called after all items were retrieved.
In case of flatMap(), completion of each inner Observable, simply will signal the source Observable to stop flatting item from the inner Observable to the source Observable, flatMap() merges items from the inner Observable as long as this stream sends items, so it's basically consume the entire inner Observable stream into the source stream, the entire stream is until termination event3 like onComplete(), so in case where inner Observable can emit more than 1 item, that means that it will make more than 1 emission on the source stream.
I'm having a cast problem inside the subscribe method, i don't know why the new Observer is giving this issue.
Observable<GradeModel> getGrade = retrofit
.create(GradeService.class)
.getGrade()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.map(model -> {
// transform model
DecimalFormat grades = (DecimalFormat) model.getGrades();
return grades;
})
.subscribe(new Subscriber<DecimalFormat>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Log.i(TAG, "onError method of observer");
}
#Override
public void onNext(DecimalFormat grades) {
mainPresenter.setListGrades(grades);
}
});
Required:
rx.Observable
Found:
rx.Subscription
Up until .subscribe(...) it is an Observable. However after subscribing it returns a Subscription instance which can be used to cancel the subscription.
The exception occurs because you are casting this Subscription back into an Observable although they are completely unrelated.
Without knowing your intentions with that variable it is hard to say what the correct code would be.
i don´t get the right way. The Problem is, that i have two Retrofit API´s and Services and need two wait for response and make a decision and based on this other calls.
Example:
APIx:
#GET("xyz/{id}/exists")
Observable<Exists> checkObjectExists(#Path("id") String id);
#POST("xyz/")
Observable<Object> addObjectA(#Body Object a);
APIy:
#POST("abc/{id}/blabla")
Observable<Object> addObjectB(#Path("id") String id, #Body Object b);
Now the Use case:
I need to do a Request if some Object exists like:
serviceA.exists(id).flatMap(exists -> if(exists) ...
if Exists is true then i need to call
serviceB.addObjectB(b)
Then the first flow is finish.
if Exists is false i need to call
serviceA.addObject(a)
and then when the i get a Success in onNext i need to call
ServiceB.addObject(b)
again. But i really dont get the Chain with RxJava and Retrofit. I can handle this stuff with a lot lines of Code with something like this:
private void _checkExists() {
ServiceA serviceA= ServiceA.create();
serviceA.checkObjectExists(id)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<Exists>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Exists exists) {
if(exists) {
_addObjectB(b);
} else {
addobjectA(a);
}
}
});
}
private void addObjectA(Object a) {
ServiceA serviceA= ServiceA.create();
serviceA.addObjectA(a)
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Object a) {
addObjectB();
}
});
}
private void addObjectB() {
ServiceB serviceB= ServiceB .create();
serviceB.addObjectB(id, b)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Object>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
Timber.d("Something get Wrong");
}
#Override
public void onNext(Object b) {
Timber.d("Everything is ok");
}
});
}
I tried to chain everything together with flatMap and so on, but it works not correctly in the Chain and also the ErrorHandling was strange, because when i get some Error from the Backend my app will Crash, this is why i add every call the Action3 with OnComplete,OnError,OnNext. Is there a possibility to do this with less code?
Thanks
What have you try with flatMap ?
regarding your code, it seams that you call checkObjectExists then, depending of the result addObjectA then addObjectB or only addObjectB.
So, it can be achieve like this :
ServiceA serviceA= ServiceA.create();
ServiceA serviceB= ServiceB.create();
serviceA.checkObjectExists(id)
.flatMap(exists -> {
if(exists) {
return serviceB.addObjectB(id, b)
} else {
return serviceA.addObjectA(id, a).flatMap(newA -> serviceB.addObject(id, newA);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe();
(as it miss some part of your code, it's hard to undertand from where a, b and other variables come from.
As you can see, it only rely on flatMap and may be closed to what you already try.
Declaration
#GET("api/Game/SearchGames")
Observable<List<GameModel>> searchGames();
This is the network call
public static Observable<List<GameModel>> searchGames () {
VersusAPI client = VersusServiceGenerator.createService(VersusAPI.class);
Observable<List<GameModel>> ob = client.searchGames();
return ob;
}
Here is where I implement.
mAdapterMyGames = new RecyclerViewAdapter(searchGames());
searchGames() returns rx.Observable<java.util.list<GameModel>>. How do I change that to only java.util.list<GameModel>?
You don't properly understand what is an Observable.
It is an object, to which You can subscribe() to get the result of it's operation. Usually, only when subscribing to an Observable it starts and you can get the result inside Subscriber's onNext() function.
So in your case:
Subscribe to this Observable.
Look for the result inside this subscriber's onNext function.
searchGames().subscribe(new new Subscriber<List<GameModel>>() {
#Override
public void onNext(List<GameModel> gameModels) {
//TODO make sth useful with models
}
#Override
public void onCompleted() { }
#Override
public void onError(Throwable e) { }
)
I'm using Retrofit and RxJava to perform some background tasks. Code looks like this:
public class MyLoader{
public Observable<MyData> getMyData(){
return setupHelper().flatMap(new Func1<MyHelper, Observable<MyData>>() {
#Override
public Observable<MyData> call(MyHelper myHelper) {
return queryData(myHelper);
}
});
}
private Observable<MyData> queryData(MyHelper myHelper){
...
}
private Observable<MyHelper> setupHelper(){
return Observable.create(new Observable.OnSubscribe<MyHelper>() {
#Override
public void call(final Subscriber<? super MyHelper> subscriber) {
try{
MyHelper helper = makeRetrofitCall();//Using Retrofit blocking call to get some data
subscriber.onNext(helper);
subscriber.onCompleted();
}catch(RetrofitError e){
subscriber.onError(e)
}
}
}
}
}
This fails with RetrofitError, due to NetworkOnMainThread Exception at this line:
MyHelper helper = makeRetrofitCall();//Using Retrofit blocking call to get some data
Subscribing to my Observable:
myLoader.getMyData()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<MyData>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(MyData inventory) {
}
});
According to Rx documentation flatMap doesn't operate on any background thread. My question is how do I ensure that the whole getMyData() method runs in background.
I just add observeOn(Schedulers.newThread()) before flatMap and it works!
This moves just one step in the pipeline to the background thread:
Observable<Integer> vals = Observable.range(1,10);
vals.flatMap(val -> Observable.just(val)
.subscribeOn(Schedulers.computation())
.map(i -> intenseCalculation(i))
).subscribe(val -> System.out.println(val));
Originally answered here:
https://stackoverflow.com/a/35429084/2908525
There is a good chance when you create the MyLoader object in the main thread the Observable.create be executed as well (or maybe somewhere else before in your code (?) ). If it's so, the .subscribeOn(Schedulers.io()) will have no effect on changing the thread.
You can try wrap the .create() with a .defer() to make sure the Observable is created only when it's subscribed.
e.g. defer(() -> create(....))