I am trying to implement a feed app. I want to make the network call for new feed on Splash Activity which will run for a maximum of 3 seconds (irrespective of feed network call gets completed or not) and then move to my home screen where feed is visible to the user. I want the response of the call i made in Splash to be returned to my Home activity. I am using Retrofit with RxJava to make network calls. I have tried to use ConnectableObserver with replay operator to try this, but it is not working.
In SplashActivity's onCreate i call a method
MyClientNetworkUtil.initObservable();
The MyClientNetworkUtil class has following code
public class MyClientNetworkUtil {
private static MyClient client = ServiceGenerator.generateService(MyClient.class);
private static ConnectableObservable<List<DataModel>> x;
public static void initObservable() {
Observable<List<DataModel>> o = client.dataForUser("user");
x = o.subscribeOn(Schedulers.io()).publish();
x.connect();
}
public static ConnectableObservable<List<DataModel>> getObservable() {
return x.replay(1);
}
}
Then in my HomeActivity's onCreate function, i am trying to do :
ConnectableObservable<List<DataModel>> x = MyClientNetworkUtil.getObservable();
x.connect();
x.subscribe(response -> Log.d("Response", response.get(1).getName()), e -> Log.e("Response", e.toString()));
But none of the above log statements are executed.
Can you please help with what i am doing wrong here.
Thanks.
You have mistake of using ConnectableObservable.
connect method instructs it to begin begin emitting the items from
its underlying Observable to its Subscriber.
So you should subscribe first and then connect.
This code will work:
public static void main(String[] args) throws Exception {
initObservable();
getObservable().forEach(System.out::println);
x.connect();
Thread.sleep(1000);
}
private static ConnectableObservable<List<Integer>> x;
public static void initObservable() {
Observable<List<Integer>> o = Observable.range(1, 50).buffer(10);
x = o.subscribeOn(Schedulers.io()).publish();
// x.connect();
}
public static Observable<List<Integer>> getObservable() {
return x.replay(1).refCount();
}
But we have better way than connect. We can use refCount or autoConnect.
refCount
Returns an Observable that stays connected to this ConnectableObservable as long as there is at least one subscription to this ConnectableObservable.
autoConnect
Returns an Observable that automatically connects to this
ConnectableObservable when the first Subscriber subscribes.
Related
What is the right way in RxJava to Subscribe to an observable that continuously receives events from different sources at unknown times.
For example: Say we have tasks that are received from a server and the calls to the server could be started from a number of different areas (eg triggered by a push notification, a polling event, user interaction etc).
The only thing we care on the UI is that we are notified of the tasks that have been received. We don't care where they come from. I effectively want to start observing for the life of the Activity and the model updates the observer as required
I have implemented the below class that does what I want, but I'm not sure if it the right way or if RxJava already accounts for something like this.
This class effective creates on ConnectableObservable that can have many observers subscribe to the one Observable(ensure all observers get the same stream). One thing I have noticed is calling observeOn and subscribeOn can cause unexpected results when subscribing to a ConnectableObservable in this fashion which can be a problem as the class can't control who is using the ConnectableObservable.
public class ApiService {
private Emitter<String> myEmitter;
private ConnectableObservable myObservable;
public ApiService() {
//Create an observable that is simply used to get the emitter.
myObservable = Observable.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
myEmitter = e;
}
}).publish();
//connect must be called here to ensure we have an instance of the emitter
// before we have any subscribers
myObservable.connect();
}
/**
* This method returns the observable that all observers will subscribe to
*
* #return
*/
public Observable<String> getObservable() {
return myObservable;
}
/**
* This method is used to simulate a value that has been received from
* an unknown source
*
* #param value
*/
public void run(final String value) {
Observable.create(new ObservableOnSubscribe<String>() {
#Override
public void subscribe(ObservableEmitter<String> e) throws Exception {
myEmitter.onNext(value);
//api call
}
}).subscribe();
}
}
I'm also curious if there are any concerns with memory leaks doing it this way assuming each observer subscribing to the one Observer is disposed of at the appropriate time.
This is the refactored class after seeing Bob's answer
public class ApiService {
private PublishProcessor<String> myProcessor = PublishProcessor.create();
public void subscribe(Subscriber<String> subscriber) {
myProcessor.subscribe(subscriber);
}
/**
* This method is used to simulate a value that has been received from
* an unknown source
*
* #param value
*/
public void run(final String value) {
myProcessor.onNext(value);
}
}
Use a Subject (RxJava) or Processor (RxJava 2) to do the subscription. You would then subscribe the subject to each source observable. Eventually, you would subscribe to the subject and get the combined stream of emissions.
Alternatively, you could use a Relay to isolate the downstream observers from any onComplete() or onError() that might come from upstream. This is a better choice when any of the observables might complete before the others.
I have this class with these structure and i need test the behaviour of OnRequestListOfLunchsFinished interface
#Override
public void getListOfLunchs(final OnRequestListOfLunchsFinished callback) {
zip().onErrorResumeNext(new Function<Throwable, ObservableSource<? extends LunchServiceResponse>>() {
#Override
public ObservableSource<? extends LunchServiceResponse> apply(#NonNull Throwable throwable) throws Exception {
callback.onError(new RuntimeException(throwable));
callback.onEnd();
return Observable.empty();
}
}).subscribe(new Consumer<LunchServiceResponse>() {
#Override
public void accept(LunchServiceResponse response) throws Exception {
List<Lunch> result = new ArrayList<>();
List<IngredientResponseVO> ingredients = response.getIngredients();
Map<Integer, Ingredient> hash = new HashMap<Integer, Ingredient>();
for (IngredientResponseVO vo : ingredients)
hash.put(vo.id, new Ingredient(vo.id, vo.name, new BigDecimal(vo.price.toString()), vo.image));
for(InfoLunchResponseVO vo: response.getLunch()){
Lunch lunch = new Lunch();
lunch.setId(vo.id);
lunch.setImage(vo.image);
lunch.setName(vo.name);
for(Integer id : vo.ingredients){
Ingredient ingredient = hash.get(id);
lunch.addIngredient(ingredient);
}
result.add(lunch);
}
callback.onSuccess(result);
callback.onEnd();
}
});
callback.onStart();
}
private Observable<LunchServiceResponse> zip(){
return Observable.zip(getRequestOfListOfLunchs(), getRequestOfListOfIngredients(), new BiFunction<List<InfoLunchResponseVO>, List<IngredientResponseVO>, LunchServiceResponse>() {
#Override
public LunchServiceResponse apply(#NonNull List<InfoLunchResponseVO> infoLunchResponseVOs, #NonNull List<IngredientResponseVO> ingredientResponseVOs) throws Exception {
return new LunchServiceResponse(infoLunchResponseVOs, ingredientResponseVOs);
}
});
}
i have this test method
#Test
public void teste(){
List<IngredientResponseVO> ingredients = Collections.emptyList();
List<InfoLunchResponseVO> lunchs = Collections.emptyList();
when(mockApi.getListOfIngredients()).thenReturn(Observable.just(ingredients));
when(mockApi.getLunchs()).thenReturn(Observable.just(lunchs));
mockImplementation.getListOfLunchs(callback);
InOrder order = inOrder(callback);
order.verify(callback).onStart();
order.verify(callback).onSuccess(anyList());
order.verify(callback).onEnd();
order.verifyNoMoreInteractions();
}
but i am receiving the exception:
org.mockito.exceptions.verification.VerificationInOrderFailure:
Verification in order failure
Wanted but not invoked:
callback.onSuccess(<any>);
if i do this:
callback.onStart();
callback.onSuccess(Collections.<Lunch>emptyList());
callback.onEnd();
InOrder order = inOrder(callback);
order.verify(callback).onStart();
order.verify(callback).onSuccess(anyList());
order.verify(callback).onEnd();
order.verifyNoMoreInteractions();
this works.
how verify only calls of my mock callback?
You just must not use the InOrder object.
mockImplementation.getListOfLunchs(callback);
Mockito.verify(callback).onStart();
Mockito.verify(callback).onSuccess(anyList());
Mockito.verify(callback).onEnd();
Mockito.verifyNoMoreInteractions();
AFAICS the issue is not with the test but with your reading of the test results (jumping ahead: I believe it found a bug in your code).
Probably in the real code your getListOfIngredients and getLunchs do some network requests i.e. they are asynchronous to the call to getListOfLunchs and (zip inside of it). Thus in the real code onStart is called immediately on the caller thread while onSucess and onEnd are called later. However in your test you mock those API calls with very synchronous Observable.just and thus the order of execution is different: first onSuccess is called, then onEnd and finally onStart (you can easily validate this if you substitute your mocked callback with a custom one that just logs method name in every call).
You probably expeceted that since you use verifyNoMoreInteractions you would get a error about wrong order of onStart. Unfortunatelly this is not how it works. Since your order verifications are specified earlier, they are checked earlier. And in those checks there is yet no restriction of "no more". So what happens is roughly following:
onSucess is called. InOrder check ignores it because there was no onStart yet
onEnd is called. InOrder check ignores it because there was no onStart yet
onStart is called. This matches what InOrder expects and now it waits for onSucess. However this (second) onSuccess never comes and this is exactly what the error says.
So what to do? First of all I'd like to say that IMHO this failed test did find a very real bug in your code. Assume that at some point in the future someone added a caching layer to your API so sometimes getListOfIngredients and getLunchs return immediately with a synchronous result. In such case your code breaks contract of the OnRequestListOfLunchsFinished that onStart should be called first. So the proper way is to fix your code. An obvious but possible wrong way is to move the line
callback.onStart();
to the start of the method. (Why it is possibly wrong? Can your zip throw an Exception? If it does, what happens to the state of the callback?). Another way is to do the same as you do with onEnd i.e. copy it inside both success and error handling code in proper order.
I am currently just using EventBus to transfer data from FirebaseMessagingService onMessageReceived to MainActivity , but this is getting tricky as complexity goes on and what if i get multiple notifications ? on the other hand,
Data transfer is costing 1 extra class and 2 boiler plate function due to EventBus.
So Problem is how to transfer data from FirebaseMessagingService to Activity using Rxjava , and is there any way to convert this whole service to some observables ?
You would still need to Service to receive the notifcations. However, you could use a PublishSubject to publish items like this:
class NotificationsManager {
private static PublishSubject<Notification> notificationPublisher;
public PublishSubject<Notification> getPublisher() {
if (notificationPublisher == null) {
notificationPublisher = PublishSubject.create();
}
return notificationPublisher;
}
public Observable<Notification> getNotificationObservable() {
return getPublisher().asObservable();
}
}
class FirebaseMessagingService {
private PublishSubject<Notification> notificationPublisher;
public void create() {
notificationPublisher = NotificationsManager.getPublisher()
}
public void dataReceived(Notification notification) {
notificationPublisher.onNext(notification)
}
}
class MyActivity {
private Observable<Notification> notificationObservable;
public void onCreate(Bundle bundle) {
notificationObservable = NotificationsManager.getNotificationObservable()
notificationObservable.subscribe(...)
}
}
Edit: expanded the example. Please note this is not the best way to do it, just an example
yes, you can convert Service for using Observables by using PublishSubject . Just return it as observable by
subject.asObservable() and pass it new event from onEvent() method by
subject.onNext().
Use Service binding to bind your Service to Activity, and , whithing binding interface, return references to your subjects as observables.
PublishSubject<String> eventPipe = PublishSubject.create();
Observable<String> pipe = eventPipe.observeOn(Schedulers.computation()).asObservable();
// susbcribe to that source
Subscription s = pipe.subscribe(value -> Log.i(LOG_TAG, "Value received: " + value));
// give next value to source (use it from onEvent())
eventPipe.onNext("123");
// stop receiving events (when you disconnect from Service)
if (s != null && !s.isUnsubscribed()){
s.unsubscribe();
s = null;
}
// we're disconnected, nothing will be printed out
eventPipe.onNext("321");
Im exactly having this behavior
Subscriber OnComplete called twice
(which is is anticipated as per http://reactivex.io/documentation/subject.html)
But in my scenario : it goes something like this :
I have a AudioRecordingService which displays a notification, in which I have options for the user to save or delete the on going recording, which is working perfectly. But I'm trying to get into using RxAndroid, my notification's save button would trigger..
RxEventBus.getInstance().postEvent(new RxEvents(RxEventsEnum.AUDIO_STOP_AND_SAVE));
which triggers
bindUntilActivitySpecificEvent(RxEventBus.getInstance().forEventType(RxEvents.class),ActivityEvent.DESTROY).subscribeOn(
AndroidSchedulers.mainThread()).subscribe(new Action1<RxEvents>() {
#Override public void call(RxEvents rxEvents) {
onEvent(rxEvents);
}
});
and in my onEvent(rxEvent) based on the rxEvents object's data I appropriately save and store recording. The first time I try this, it works fine, but the subsequent times, the
#Override public void call(RxEvents rxEvents) {
onEvent(rxEvents);
}
is being called multiple times, like for example the second time I post an event, this callback is called twice, the third time thrice and so on... (which is actually what PublishSubject does). I don't want this behavior, I want Rx to be a able to post events and receive only the latest event that was posted and nothing else.
Here is my other relevant code
protected final <T> Observable<T> bindUntilActivitySpecificEvent(Observable<T> observable,
ActivityEvent event) {
return observable.compose(RxLifecycle.<T, ActivityEvent>bindUntilEvent(lifecycle(), event))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
}
and my run of the mill RxEventBus class :
public class RxEventBus {
private static final RxEventBus INSTANCE = new RxEventBus();
public static RxEventBus getInstance() {
return INSTANCE;
}
private RxEventBus() {
}
private final Subject<Object, Object> mBus = new SerializedSubject<>(PublishSubject.create());
public void postEvent(Object event) {
mBus.onNext(event);
}
public <T> Observable<T> forEventType(Class<T> eventType) {
return mBus.ofType(eventType).observeOn(AndroidSchedulers.mainThread());
}
}
What is the best approach using RxAndroid ? Please note that I am looking for RxAndroid solution only.
You are creating a new observable every time you trigger an event in
RxEventBus.getInstance().forEventType(RxEvents.class)
You need to cache the observables you create for each event type.
I am new in Android and Retrofit and I am facing one problem.
I want to have my lets say "ServerCommunication" class (singelton) where all Retrofit magic is done and it will have public methods where REST calls are done.
I want to use this "ServerCommunication" instance in my activities to call Rest service, but thats it. Application logic should be done in activity. So this way some activity Login calls method Login(POJORequest) in "ServerCommunication) where actual REST call via Retrofit framework is done and some POJOResponse is returned. So Activity doesn't care about REST communication while ServerCommunication doesn't care about what logic that should be applied to response from REST service since.
With retrofit 2 I do not understand how I can block Activity to wait for response from retrofit and how it can be returned. Well, I might think I can use some callback methods in activity so those methods can be called from ServerCommunication" in OnPostExecute() to apply some logic based on data from response. It's just I think it should be simpler approach.
Well, to clarify all this mess above imagine simple case: You have data in you main activity, you pass this data to your communication class where REST call is done and response is received. This response must be validated in order to continue. And you want this validation to be done in main activity and NOT in communication class.
What is pattern to do that in Android with Retrofit2 ?
Thank you in advance
What I normally do:
Create your Interface (where you have all your REST methods - GET & POST etc)
Create a class that does the actual calls with corresponding methods (refer to the interface REST methods). I would call it something like ServiceAPIImplementor. This is where you actually create your Retrofit adapter.
In your activity, create an instance of your implementor class and call the methods and pass the expected arguments.
After calling the methods, you should probably show a progress dialog to let the user know that something is going on.
When the onResponse or onFailure method is called, use an Event pattern (EventBus library?) to notify the activity that the network operation has been completed. Once the activity has received the notification, it should then dismiss the progress dialog and update the UI accordingly - with the newly received data or completed operation (expected outcome).
I hope this helps you get closer to what you are trying to achieve!
Service interface (IPhotoService):
#GET("/photos/kudos")
Call<String> fetchKudos(#Header("Authorization") String authorization,
#Query("offset") int offset, #Query("mt") boolean mt);
Service impl (PhotoService):
private GoApiProvider<IPhotoService> mGoProvider = new GoApiProvider<>();
public Promiser<List<Photo>, HttpError> fetchKudos() {
return new Promiser<>((resolve, reject) ->
mGoProvider.getService(IPhotoService.class).fetchKudos(mSession.getToken(),
mOffsetKudos, true).enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
PhotoParser JSON = new PhotoParser();
try {
mOffsetKudos = mOffsetKudos + 20;
resolve.run(JSON.photosFromJson(response.body()));
} catch (JSONException e) {
Log.e("fetchKudos", e.toString());
}
} else {
reject.run(new HttpError(response.code(), response.message()));
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
reject.run(new HttpError(YPErrorType.Undefined.getType(), t.getMessage()));
}
})
);
}
Activity or Fragment :
private void loadPhoto() {
new PhotoService().fetchKudos()
.success(this::resultSucceeded)
.error(this::resultError);
}
private void resultSucceeded(List<Photo> photos) {
mPhotoAdapter.setItems(photos);
}
private void resultError(HttpError httpError) {
httpErrorToast(httpError);
}
If you want to use Promizer: Click here