RxJava - ReplaySubject only emitting data twice - android

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>'

Related

MVVM architecture problem when trying to get my item details?

Here I am not asking for code problem, but just architecture, I will show what Iof course.
So I was just playing around with this MVVM architecture, I was displaying a list of items, I click an item I see its details, there are many categories in those details, meaning that for example I had a personal data displayed in one cardview, informations about his credit card for example in an other cardview, etc.. For some Users who are connected, credit cards data must not be displayed, but when I created the view model in the begging it was like this :
public class ContractViewModel extends ViewModel {
public MutableLiveData<ContractModel> contract = new MutableLiveData<ContractModel>();
public MutableLiveData<Boolean> isLoading = new MutableLiveData<Boolean>();
public MutableLiveData<Boolean> error = new MutableLiveData<Boolean>();
#Inject
ContractService contractService;
CompositeDisposable disposable = new CompositeDisposable();
public ContractViewModel(){
DaggerContractApiComponent.create().inject(this);
}
public void call(SingleContractRequest singleContractRequest){
fetchContract(singleContractRequest);
}
public void fetchContract(SingleContractRequest singleContractRequest){
isLoading.setValue(true);
disposable.add(
contractService.getContract(singleContractRequest)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<ContractModel>() {
#Override
public void onSuccess(ContractModel contractModel) {
isLoading.setValue(false);
error.setValue(false);
contract.setValue(contractModel);
}
#Override
public void onError(Throwable e) {
error.setValue(true);
isLoading.setValue(false);
e.printStackTrace();
}
})
);
}
#Override
protected void onCleared(){
super.onCleared();
disposable.clear();
}
}
But now I need the user connected role in the app to display the possible data that he can consult. The first thought coming to my head was just calling the service that return my userConnected.
public class ContractViewModel extends ViewModel {
public MutableLiveData<ContractModel> contract = new MutableLiveData<ContractModel>();
public MutableLiveData<UserConnected> userConnected = new MutableLiveData<UserConnected>();
public MutableLiveData<Boolean> isLoading = new MutableLiveData<Boolean>();
public MutableLiveData<Boolean> error = new MutableLiveData<Boolean>();
#Inject
ContractService contractService;
#Inject
UserConnectedService userConnectedService;
CompositeDisposable disposable = new CompositeDisposable();
public ContractViewModel(){
DaggerContractApiComponent.create().inject(this);
DaggerUserConnectedApiComponent.create().inject(this);
}
public void call(SingleContractRequest singleContractRequest){
fetchContract(singleContractRequest);
}
public void getUserConnected(){
disposable.add(
userConnectedService.getUserConnected()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<UserConnected>() {
#Override
public void onSuccess(UserConnected userConnectedParams) {
userConnected.setValue(userConnectedParams);
}
#Override
public void onError(Throwable e) {
}
})
);
}
public void fetchContract(SingleContractRequest singleContractRequest){
isLoading.setValue(true);
disposable.add(
contractService.getContract(singleContractRequest)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableSingleObserver<ContractModel>() {
#Override
public void onSuccess(ContractModel contractModel) {
isLoading.setValue(false);
error.setValue(false);
contract.setValue(contractModel);
}
#Override
public void onError(Throwable e) {
error.setValue(true);
isLoading.setValue(false);
e.printStackTrace();
}
})
);
}
#Override
protected void onCleared(){
super.onCleared();
disposable.clear();
}
}
But it is not clean, I have to call two methods at the same time in my Activity, which is terrible, the second thought is that I have to edit my contract type from MutableLiveData<ContractModel> to something like this MutableLiveData<NewModel> knowing that this NewModel architecture is like this :
public NewModel {
ContractModel contractModel;
UserModel userModel;
// ofc here a constuctor and getters and setters
}
But this has a problem too that will be caused in the backend, because what if I need the userConnected in an other activity ? I will have to write similar code in multiple places to get this userConnected, but I want to get it once.
Any help, thoughts would be really helpful.

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

RxAndroid not returning Subcription for the observer

I have started learning RxAndroid and below is the code I wrote to iterate over a model object (Results) that contains data fetched from the server. I'm iterating over the model object in the observable and providing a newly created object in the observer. I'm trying to take subscription of the observer to unsubscribe the task upon Orientation changes of the fragment. However the subscribe() returns VOID instead of subscription object.
Questions:
Does the latest version of RxAndroid handle unsubscription itself upon configuration/orientation change?
In case configuration change happens before the task is complete, the only way to restart this task that I can think of is, I persist the server response in onSavedInstance() and retrieve it from bundle when the fragment is recreated. It'll require booleans to figure out if the configuration change happened before the configuration change or not. Is there a graceful and cleaner way of coping with this?
private void createComicList(final List<Result> marvelResults) {
final MarvelComics marvelComics = new MarvelComics();
Observable marvelObservable2 = Observable.create(new ObservableOnSubscribe<MarvelComic>() {
#Override
public void subscribe(ObservableEmitter<MarvelComic> e) throws Exception {
for(Result result : marvelResults) {
MarvelComic marvelComic = new MarvelComic();
marvelComic.setDescription(result.getDescription());
marvelComic.setTitle(result.getTitle());
marvelComic.setPageCount(result.getPageCount());
marvelComic.setThumbnailUrl(result.getThumbnail().getPath());
marvelComic.setId(result.getId());
e.onNext(marvelComic);
}
e.onComplete();
}
});
marvelObservable2.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MarvelComic>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(MarvelComic comic) {
marvelComics.getMarvelComicList().add(comic);
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
showToast();
}
});
}
The Observable.subscribe(Observer<? super T>) method returns void in the 2.x since the Observer.onSubscribe(Disposable) is there to get the cancellation support that used to be Subscription in 1.x.
final CompositeDisposable composite = new CompositeDisposable();
Observable<Integer> source = Observable.just(1)
source.subscribe(new Observer<Integer>() {
#Override public void onSubscribe(Disposable d) {
composite.add(d); // <---------------------------------------------
}
#Override public void onNext(Integer t) {
System.out.println(t);
}
#Override public void onError(Throwable e) {
e.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done");
}
});
composite.add(source
.subscribeWith( // <-----------------------------------------------
new DisposableObserver<Integer>() {
#Override public void onNext(Integer t) {
System.out.println(t);
}
#Override public void onError(Throwable e) {
e.printStackTrace();
}
#Override public void onComplete() {
System.out.println("Done");
}
});
subscribe() method of Observable returns Subscription object in earlier versions of RxJava and current version returns an object of Disposble class which you can unsubscribe by invoking dispose() method.
For your second question you may check this answer Best practice: AsyncTask during orientation change

RxJava subscriber as a parameter does not work, new identical subscriber work

We're using rxandroid:1.1.0 along with rxjava:1.1.2 in our application.
Our project has the RxJava Subscriber created this way:
Android Activity creates the Subscriber. When user deletes an item we pass the Subscriber as argument to a "network service" class we have
The network service class sets up the Observable, and gets the Subscriber as argument. The Observable is set to subscribe to the Subscriber argument
This unfortunately doesn't work. The subscriber never gets called and we don't get errors or any more information.
However, if instead of using the argument Subscriber, and I create a brand new Subscriber of the same time, the Observable works just fine.
Any idea how I could make this work with the argument Subscriber? We want to call UI methods onComplete from the Activity that are not accessible from the network service class.
Here's my activity code:
public Subscriber<Models.FavoriteResponse> mFavoritesSubscriber = new Subscriber<Models.FavoriteResponse>() {
#Override
public void onCompleted() {
// empty
Logger.w("mFavoritesSubscriber onCompleted");
showFavorites();
}
#Override
public void onError(Throwable e) {
Logger.w("mFavoritesSubscriber onError");
}
#Override
public void onNext(Models.FavoriteResponse favoriteResponse) {
Logger.w("mFavoritesSubscriber onNext");
getHelper().setIsFavoriteOnProducts(favoriteResponse.getFavoriteResponseList());
}
};
private void removeSelectedFavorites() {
for (Product selectedProduct : selectedProducts) {
ServiceUtils.removeFavorite(this, selectedProduct.sku, mFavoritesSubscriber);
}
}
And here's the code from my network service class.
This version does not work:
public static void removeFavorite(final Context context, final String sku, Subscriber<Models.FavoriteResponse> subscriber) {
Logger.w("ServiceUtils removeFavorite");
String userId = PersistentSharedPreferences.readUserId(context);
FavoritesServices service = FavoritesServices.Companion.create(BuildConfig.ORDER_SERVICE_DOMAIN,
new Interceptor(context));
final Observable<Models.FavoriteResponse> observable = service
.deleteFavorite(userId, sku, getFavoritesParameters(context));
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorResumeNext(new Func1<Throwable, Observable<? extends Models.FavoriteResponse>>() {
#Override
public Observable<? extends Models.FavoriteResponse> call(Throwable throwable) {
Logger.w("ServiceUtils onErrorResumeNext");
return null;
}
})
.subscribe(subscriber);
}
But this version of the network service class method works fine. Notice the new Subscriber:
public static void removeFavorite(final Context context, final String sku, Subscriber<Models.FavoriteResponse> subscriber) {
Logger.w("ServiceUtils removeFavorite");
String userId = PersistentSharedPreferences.readUserId(context);
FavoritesServices service = FavoritesServices.Companion.create(BuildConfig.ORDER_SERVICE_DOMAIN,
new Interceptor(context));
final Observable<Models.FavoriteResponse> observable = service
.deleteFavorite(userId, sku, getFavoritesParameters(context));
observable.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.onErrorResumeNext(new Func1<Throwable, Observable<? extends Models.FavoriteResponse>>() {
#Override
public Observable<? extends Models.FavoriteResponse> call(Throwable throwable) {
Logger.w("ServiceUtils onErrorResumeNext");
return null;
}
})
.subscribe(new Subscriber<Models.FavoriteResponse>() {
#Override
public void onCompleted() {
Logger.w("ServiceUtils Subscriber onCompleted");
}
#Override
public void onError(Throwable e) {
Logger.w("ServiceUtils Subscriber onError");
}
#Override
public void onNext(Models.FavoriteResponse favoriteResponse) {
Logger.w("ServiceUtils Subscriber onNext");
}
});
}
Any help would be appreciated. Thank you!
Don't reuse a Subscriber. Subscriber is a Subscription and it's stateful. Subscriber won't be notified after it's unsubscribed.
You can use Observer instead.

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