How chain Retrofit calls? - android

How can I make one retrofit 2 call after another?
I'm reading about RxJava and I'm already doing my calls using RxJava, but I havn't found a good exemple of how to use flatMaps.
Can someone explain how to do it to me?
I'm trying to make these two calls, and after they're both done, I want to start a new activity.
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://api.openweathermap.org/data/2.5/")
.build();
WeatherService weatherService = retrofit.create(WeatherService.class);
final Observable<Weather> london = weatherService.getCurrent();
london.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Weather>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Weather weather) {
Log.i("WEATHER","Weather Name: " + weather.getName());
}
});
final Observable<Wind> windObservable = weatherService.getWind();
windObservable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<Wind>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(Wind wind) {
Log.i("WEATHER","Wind: " + wind.getSpeed().toString());
}
});
}
}

Maybe this link: https://github.com/ReactiveX/RxJava/wiki/Combining-observables will help. Checkout for zip. Eventually switchMap method may be useful in Your case.
Edit:
Maybe this example http://joluet.github.io/blog/2014/07/07/rxjava-retrofit/ will help You even more.
Edit #2: Some code
login().switchMap(new Func1<FirstResponse, Observable<SecondResponse>>() {
#Override
public Observable<SecondResponse> call(FirstResponse t) {
if (ApiUtils.isLoginValid(t)) {
return profile(t.getToken());
}
else{
return Observable.error(new CustomException());
}
}
}
}).subscribe(subscriber());
Note: profile method return type is is Observable<SecondResponse> and subscriber method type is Subscriber<? super SecondResponse>

You can either use flatMap as in the example or concatWith:
static Observable<Integer> intObservable() {
return Observable.just(1).delay(1, TimeUnit.SECONDS);
}
static Observable<String> stringObservable() {
return Observable.interval(1, TimeUnit.SECONDS).take(2).map(v -> v.toString());
}
public static void main(String[] args) {
intObservable()
.doOnNext(System.out::println)
.ignoreElements()
.cast(Object.class)
.concatWith(stringObservable()
.doOnNext(System.out::println)
.ignoreElements())
.toBlocking()
.subscribe();
}
However, you need some cast in case the two sources have different types.

You have multiple options here. One of them is to use zip:
london.zipWith(weatherService, new Func2<Weather, Wind, Something>() {
#Override
public Something call(final Weather weather, final Wind wind) {
...
return something;
}
}).subscribeOn(Schedulers.newThread()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<Something>() {
#Override
public void onCompleted() {
}
#Override
public void onError(final Throwable e) {
}
#Override
public void onNext(final Something something) {
}
});

Related

RxJava - ReplaySubject only emitting data twice

I am new to ReactiveX and I have a case where I want my observable to emit data to a late subscriber(whenever the observer subscribes, observable should emit the same data that it emitted previously). I made this Observable class that provide ReplaySubject's same instance to all observers (it is singleton class).
public class AccountsObservable {
private static ConnectableObservable<String> hotObservable;
private static AccountsObservable accountsObservable;
public static AccountsObservable getObject() {
if (accountsObservable == null) {
accountsObservable = new AccountsObservable();
}
return accountsObservable;
}
public ConnectableObservable<String> getObservable() {
if (hotObservable == null) {
Observable<String> observable = ReplaySubject.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> emitter) throws Exception {
emitter.onNext("XYZ");
emitter.onComplete();
}
});
hotObservable = observable.replay();//publish
}
return hotObservable;
}
}
Similarly, this is the observer class that creates new observer instance.
public class AccountsObserver {
AccountsFetchListener listener;
public AccountsObserver(AccountsFetchListener listener) {
this.listener = listener;
}
public Observer<String> getObserver() {
return new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String accounts) {
listener.onSuccess(accounts);
}
#Override
public void onError(Throwable e) {
listener.onFailure();
}
#Override
public void onComplete() {
}
};
}
public interface AccountsFetchListener {
void onSuccess(String accounts);
void onFailure();
}
}
Here is the function where I test these observables
private void testObs() {
ConnectableObservable<String> observable = AccountsObservable.getObject().getObservable();
Observer<String> observer = new AccountsObserver(new AccountsObserver.AccountsFetchListener() {
#Override
public void onSuccess(String accounts) {
Log.e("DATA -> ", accounts);
}
#Override
public void onFailure() {
}
}).getObserver();
observable.subscribe(observer);
observable.connect();
}
I called this function "testObs()" 5 times but it emitted data only 2 times. The problem seems to be in AccountsObservable class where I provide ReplaySUbject's instance. Thanks
Your code runs fine as it is, your logs are being suppressed in logcat as per this:
We declared an application as too chatty once it logs more than 5 lines a second. Please file a bug against the application's owner that is producing this developer-verbose-debug-level class logging spam. The logs are 256KB, that means the application is creating a DOS attack and shortening the logs timepan to 6 seconds(!) making it useless for all others.
You can avoid this behaviour by whitelisting your app for logcat:
adb logcat -P '<pid or uid of your app>'

How replace returned observable with a new one RxJava2

I have one case when I need to return an observable immediately, but then replace this observable with another one.
Here is an example
private Flowable<byte[]> mFlowableStream = Flowable.empty();
#Override
public Flowable<byte[]> startStreamRead() {
bindToService();
return mFlowableStream;
}
And then after binding to service I provide it a callback connection like that
#Override
public void bindToService() {
mAppContext.bindService(new Intent(mAppContext,StreamService.class), mServiceConnection, 0);
}
#Override
public void onServiceConnected(ComponentName name, IBinder binder) {
mServiceInterection = ((StreamServiceInterection.LocalBinder) binder).getServiceInteractor();
mStreamDisposable = mServiceInterection.getStream()
.subscribe(new Consumer<byte[]>() {
#Override
public void accept(byte[] data) throws Exception {
}
});
}
What I want to do is to somehow replace returned previously mFlowableStream with a new observable that I got from service.
What are possible strategies to implement this ? Maybe I should return some other value, like Future.
Please suggest how to solve this problem
Thanks
You can use Flowable.create instead of Flowable.empty
Then when new data come, just push to flowable.
Like Example
final ArrayList<FlowableEmitter<Integer>> arrEmitter = new ArrayList<>();
Flowable<Integer> futureFlow = Flowable.create(new FlowableOnSubscribe<Integer>() {
#Override
public void subscribe(final FlowableEmitter<Integer> e) throws Exception {
e.onNext(1);
e.onNext(2);
arrEmitter.add(e); // hold emitter to use later
}
}, BackpressureStrategy.BUFFER);
futureFlow.subscribe(new ResourceSubscriber<Integer>() {
#Override
public void onNext(Integer integer) {
System.out.println("onNext: " + integer);
}
#Override
public void onError(Throwable t) {
}
#Override
public void onComplete() {
System.out.println("onComplete");
}
});
// =========== When data come
FlowableEmitter<Integer> holdEmitter = arrEmitter.get(0);
holdEmitter.onNext(3);
Or use you can use **Subject* type according to your need
Understanding RxJava Subject — Publish, Replay, Behavior and Async Subject

Android rxjava2 subscription within OnClick

I'm trying to dig into the wonders of the RxJava2 world, but I'm still pretty confused.
Basically I have to call an API when the user clicks on a button, so I'm using a Retrofit2 client which returns an Observable that I subscribe on the on click method of the button.
The issue is that when the button is clicked twice I'll get:
io.reactivex.exceptions.ProtocolViolationException: It is not allowed to subscribe with a(n) <package>.MainActivity$1 multiple times. Please create a fresh instance of <package>.MainActivity$1 and subscribe that to the target source instead.
If I dispose the observer after the onComplete the api won't be called as the subscription is invalidated.. Am I missing/misunderstanding something?
public class MainActivity extends AppCompatActivity {
#BindView(R.id.button) Button button;
private DisposableObserver<PopularGames[]> observer;
private Observable<PopularGames[]> popularGamesObservable;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
APIsInterface apiClient = MyApplication.getInstance(this).getApiClient();
popularGamesObservable = apiClient.getPopularGames();
observer = new DisposableObserver<PopularGames[]>() {
#Override
public void onNext(PopularGames[] result) {
Timber.d("onNext " + Arrays.asList(result));
}
#Override
public void onError(Throwable e) {
Timber.e("onError " + e);
}
#Override
public void onComplete() {
Timber.d("onComplete");
}
};
}
#OnClick(R.id.button)
public void onViewClicked() {
popularGamesObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(observer);
}
}
io.reactivex.exceptions.ProtocolViolationException is expected
What you can do
CompositeDisposable compositeDisposable = new CompositeDisposable();
Then
#OnClick(R.id.button)
public void onViewClicked() {
compositeDisposable.add( popularGamesObservable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableObserver<PopularGames[]>() {
#Override
public void onNext(PopularGames[] result) {
Timber.d("onNext " + Arrays.asList(result));
}
#Override
public void onError(Throwable e) {
Timber.e("onError " + e);
}
#Override
public void onComplete() {
Timber.d("onComplete");
}
}));
}
Then in onDestory
compositeDisposable.dispose();

Is it possible to get 2 values in onNext() of subscriber in rxjava android?

I've an observable like this
Observable.zip(observable, extObs, new Func2<List<UserProfile>, ArrayList<Extension>, UserProfile>() {
#Override
public UserProfile call(List<UserProfile> userProfiles, ArrayList<Extension> extensions) {
return userProfiles.get(0);
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).unsubscribeOn(Schedulers.io()).subscribe(new Subscriber<UserProfile>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(UserProfile userProfile) {
profileListener.onProfileSet(userProfile);
}
});
}
I need to pass the ArrayList in the methodprofileListener.onProfileSet(userProfile); as profileListener.onProfileSet(userProfile,extensions);
Is it possible to do so in zip or is there any other methods of rxjava to solve such type of problems?
You have to do exactly what cricket_007 suggested in the comment.
For example like this:
Create a class that would represent combined results of your observables:
class CombinedResults {
public UserProfile userProfile;
public List<Extension> extensions;
public CombinedResults(UserProfile userProfile, List<Extension> extensions) {
this.userProfile = userProfile;
this.extensions = extensions;
}
}
(Alternatively you could use Pair class)
Use an object of CombinedResults (or Pair) in your Observable.zip Func2.
Observable.zip(observable, extObs,
new Func2<List<UserProfile>, ArrayList<Extension>, CombinedResults>() {
#Override
public CombinedResults call(List<UserProfile> userProfiles, ArrayList<Extension> extensions) {
return new CombinedResults(userProfiles.get(0), extensions);
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.unsubscribeOn(Schedulers.io())
.subscribe(new Subscriber<CombinedResults>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onNext(CombinedResults combined) {
profileListener.onProfileSet(combined.userProfile, combined.extensions);
}
});

A way define order of subscribers in RxJava?

I'm looking a way to define order(?) of observers.
#GET("/get_user_msgs")
Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params);
For example I gave a Observable from my Rest API created by Retrofit.
In my ListView I'm observing this Observable.
api.getPrivateMessages(params).subscribe(new Observer());
I also have an API wrapper for my Espresso tests and I'm subscribing to same Observable there. This way observer in API wrapper is called first and only then observer in ListView
is called.
public class IdlingWrapper implements Api, IdlingResource {
....
public IdlingWrapper(Api realApi) {
this.realApi = realApi;
}
...
public Observable<PrivateMessagesResponse> getPrivateMessages(#QueryMap Map<String, String> params); {
counter.incrementAndGet();
return wrapObservable(realApi.getPrivateMessages(params));
}
protected <T> Observable<T> wrapObservable(final Observable<PrivateMessagesResponse> observable) {
//what to do here?
}
}
Is there a way to force some observer to be notified after all others are done? Or something similar in that matter?
Something like
Observable observable = getObservable();
observable.subscribeAsLast(new LastObserver());
observable.subscribe(new ObserverA());
observable.subscribe(new ObserverB());
And so that ObserverA would be notified first, then ObserverB and only then LastObserver.
Or any other approach where I could find out when all registered observers were notified and completed.
I'm not exactly sure what you are trying to do in IdlingWrapper, but I think the current implementation is very fragile.
I think the most important thing that needs to happen is to guarantee the observable can only be called once.
Here is a quick implementation to demonstrate that as well as my implementation of wrapObservable.
public class Test {
private static int counter = 0;
private static final List<Observable<?>> list = Collections.synchronizedList(new ArrayList<>());
protected static <T> Observable<T> wrapObservable(final Observable<T> original) {
// run atleast once???
synchronized (list) {
list.add(original);
}
return Observable.create(new Observable.OnSubscribe<Void>() {
#Override
public void call(Subscriber<? super Void> subscriber) {
synchronized (list) {
counter++;
if (!list.contains(original)) {
subscriber.onError(new Exception("You can only subscribe once!"));
return;
}
list.remove(original);
}
// Sleep to make it easier to see things happening...
try {
Thread.sleep(3000);
} catch (InterruptedException ignored) {
}
subscriber.onCompleted();
}
}).flatMap(new Func1<Void, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Void o) {
return original;
}
}).finallyDo(new Action0() {
#Override
public void call() {
synchronized (list) {
counter--;
if (list.size() == 0 && counter == 0) {
System.err.println("finally");
}
}
}
});
}
public static void main(String[] args) throws InterruptedException {
for(int i = 0; i < 10; i++) {
// running in io thread for simulating async call.
Observable<String> test = wrapObservable(Observable.from("TEST!!!!!!")).subscribeOn(Schedulers.io());
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
// example of calling the same observable twice.
test.subscribe(new Observer<String>() {
#Override
public void onCompleted() {
System.err.println("completed");
}
#Override
public void onError(Throwable e) {
System.err.println("error");
}
#Override
public void onNext(String s) {
System.err.println("next");
}
});
}
Thread.sleep(10000);
}
}
It seems, that this worked just fine.
protected <T> Observable<T> wrapObservable(final Observable<T> original) {
return Observable.create(new Observable.OnSubscribeFunc<T>() {
#Override
public Subscription onSubscribe(final Observer<? super T> t1) {
original.subscribe(new Observer<T>() {
#Override
public void onCompleted() {
t1.onCompleted();
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onError(Throwable e) {
t1.onError(e);
uiThreadHandler.post(new Runnable() {
#Override
public void run() {
counter.decrementAndGet();
notifyIdle();
}
});
}
#Override
public void onNext(T args) {
t1.onNext(args);
}
});
return Subscriptions.empty();
}
});
}
If you want to just use built in RxJava methods to order your observers, you can use flatMap and range to turn each item into multiple items each with a priority and then filter on priority. Observers are ordered based on how they filter.
Here's a trivial example:
Observable<Pair<Integer, Object>> shared = RxView.clicks(findViewById(R.id.textView))
.flatMap(c -> Observable.range(0, 2).map(i -> Pair.create(i, c)))
.share();
shared.filter(p -> p.first == 1)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "first subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "first subscribed onNext"));
shared.filter(p -> p.first == 0)
.map(p -> p.second)
.doOnSubscribe(c -> Log.d(TAG, "second subscribed doOnSubscribe"))
.subscribe(c -> Log.d(TAG, "second subscribed onNext"));
If you are doing this all over the place

Categories

Resources