I would like to use PublishProcessor that would work like that:
is fed with some init data (randomly when user scrolls view - Integer in the example),
it does some background job (downloads data based on the init data)
when finish downloading notifies about newly downloaded data type (String in the example)
It should be all the time ready to receive and handle new init data.
I've prepared simple code to test it but it doesn't work.
PublishProcessor processor = PublishProcessor.create();
processor.map(new Function<Integer, String>() {
#Override
public String apply(Integer o) throws Exception {
Thread.sleep(1000);
DevLog.d("test","executing "+o);
return String.valueOf(o)+"aaa";
}
}).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Subscriber<String>() {
Subscription sub;
#Override
public void onSubscribe(org.reactivestreams.Subscription s) {
DevLog.d("test","onsubscribe "+s);
sub = s;
sub.request(1);
}
#Override
public void onNext(String s) {
DevLog.d("test","next "+s);
sub.request(1);
}
#Override
public void onError(Throwable t) {
DevLog.d("test","error "+t);
}
#Override
public void onComplete() {
DevLog.d("test","complete");
}
});
processor.onNext(666);
processor.onNext(123);
processor.onNext(456);
DevLog.d("test","nextsent");
All I get in the logcat is:
onsubscribe 0
nextsent
I would rather expect:
nextsent
executing 666
executing 123
executing 456
next 666aaa
next 123aaa
next 456aaa
Related
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>'
I had a retrofit request, when I get data in onResponse,
I did multiples insert in textviews which I called heavy work in the code above, I get the result from OnReponse if there's one, else I get result from database, so the problem I had the same code in OnResponse and OnFailure, so there's any way to put my heavy work outside retrofit, and wait the response to get just one result from OnResponse or OnFailure ??
call.enqueue(new Callback<Dashboard>() {
#Override
public void onResponse(Call<Dashboard> call, Response<Dashboard> response) {
realm.beginTransaction();
dashboard = realm.copyToRealmOrUpdate(response.body());
realm.commitTransaction();
// heavy work : insert in data in multiple text views
}
#Override
public void onFailure(Call<Dashboard> call, Throwable t) {
Log.e("error ", "" + t.getMessage());
dashboard = realm.where(Dashboard.class).findFirst();
// heavy work : insert in data in multiple text views
}
}
Try this..
First Create an interface ..Let's call it OKCallback.
public interface OKCallback {
void onSuccess(String result);
void onFailure(String result);
}
Then in your method that launches the retrofit request, pass final OKCallback okCallback like this..
public void NetworkCall(final OKCallback okCallback){
...........
call.enqueue(new Callback<Dashboard>() {
#Override
public void onResponse(Call<Dashboard> call, Response<Dashboard> response) {
realm.beginTransaction();
dashboard = realm.copyToRealmOrUpdate(response.body());
realm.commitTransaction();
// heavy work : insert in data in multiple text views
okCallback.onSuccess(parcel);
}
Finally simply (ActivityX implements OKCallback) in any class or activity and you should be able to do your heavy work there..You can also wrap your data in the onSuccess methods with a Handler as shown.
#Override
public void onSuccess(String result) {
Handler handler = new Handler(ActivityX.this.getMainLooper());
//process result and
handler.post(new Runnable() {
#Override
public void run() {
//heavy work done here will run on UI thread
}
});
}
You can make an interface and get call back on main thread or after getting response of api call in onSuccess() or in onfailure() start a new AsynTask and process the request in background.
You can change it like this
//create a interface
public interface ConfirmationCallback {
void onSuccess(YourResponseClass value);
void onError();
}
//call this method from your class
yourApiCall(new ConfirmationCallback() {
#Override
public void onSuccess(YourResponseClass value) {
realm.beginTransaction();
dashboard = realm.copyToRealmOrUpdate(value);
realm.commitTransaction();
// heavy work : insert in data in multiple text views
}
#Override
public void onError() {
dashboard = realm.where(Dashboard.class).findFirst();
// heavy work : insert in data in multiple text views
}
});
public void yourApiCall(final ConfirmationCallback confirmationCallback){
call.enqueue(new Callback<Dashboard>() {
#Override
public void onResponse(Call<Dashboard> call, Response<Dashboard> response) {
confirmationCallback.onSuccess(response.body());
}
#Override
public void onFailure(Call<Dashboard> call, Throwable t) {
Log.e("error ", "" + t.getMessage());
confirmationCallback.onError();
}
}
}
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
Hi I just start learning Reactive programming using RxJava2.
How do I create a task that runs in the background thread and then complete on main thread using RxJava2.
Example in Android we use AsyncTask just like example below
private class MyTask extends AsyncTask<String, Integer, Boolean>
{
#Override
protected Boolean doInBackground(String... paths)
{
for (int index = 0; index < paths.length; index++)
{
boolean result = copyFileToExternal(paths[index]);
if (result == true)
{
// update UI
publishProgress(index);
}
else
{
// stop the background process
return false;
}
}
return true;
}
#Override
protected void onProgressUpdate(Integer... values)
{
super.onProgressUpdate(values);
int count = values[0];
// this will update my textview to show the number of files copied
myTextView.setText("Total files: " + count);
}
#Override
protected void onPostExecute(Boolean result)
{
super.onPostExecute(result);
if (result)
{
// display a success dialog
ShowSuccessAlertDialog();
}
else
{
// display a fail dialog
ShowFailAlertDialog();
}
}
}
For this example I want to pass in a Array / ArrayList of Strings and it is use to execute some method in the background thread. Then every success result will update my TextView (UI thread). If one of the process fail, I want it to stop directly. Lastly I want to update my Views when the process has completed.
I only manage to get this far
Observable.just(paths).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ArrayList<String>>()
{
private boolean result;
#Override
public void onSubscribe(Disposable d)
{
}
#Override
public void onNext(ArrayList<String> paths)
{
for (int index = 0; index < paths.size(); index++)
{
result = copyFileToExternal(paths[index]);
if (result == true)
{
// cant update UI because is in background thread
myTextView.setText("Total files: " + index);
}
else
{
// end the loop
break;
}
}
}
#Override
public void onError(Throwable e)
{
}
#Override
public void onComplete()
{
if (result)
{
// cant display because it is still in background thread
ShowSuccessAlertDialog();
}
else
{
// cant display because it is still in background thread
ShowFailAlertDialog();
}
}
});
I looked at a few tutorials but can't seem to find the answer.
Thanks in advance for the help
I would do something like this:
Observable.fromArray(getPaths())
.map(path -> copyFileToExternal(path))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(aInteger -> Log.i("test", "update UI"),
throwable -> ShowFailAlertDialog),
() -> ShowSuccessAlertDialog());
A good idea is usually to have a "handler" for controlling the subscription to your observer. So that, when you need to stop your background task (for example because the user left the Activity), you can use it. For this purpose you can use subscribeWith instead of subscribe, that receive as input a ResourceObserver: in this way you get a Disposable.
Disposable subscription = Observable.fromArray(getPaths())
.map(path -> copyFileToExternal(path))
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new ResourceObserver<Integer>() {
#Override
public void onNext(#NonNull Integer index) {
Log.i("test", "update UI");
}
#Override
public void onError(#NonNull Throwable e) {
ShowFailAlertDialog();
}
#Override
public void onComplete() {
ShowSuccessAlertDialog();
}
});
When you need to stop the task you can just call:
subscription.dispose();
I'm new at this, but I got a working example..
//Observable
Observable.just("input_parameter")
.subscribeOn(Schedulers.io())//creation of secondary thread
.map(new Function<String, String>() {//<input obj,return obj>
#Override
public String apply(String cad){//input obj
Log.d(TAG,"thread :"+Thread.currentThread().getName());
//runs in a secondary thread
return "result text: "+doLongNetworkOperation();
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(MyObserver);//now this runs in main thread
And MyObserver:
//Observer
Observer MyObserver = new Observer() {
#Override
public void onSubscribe(Disposable d) {
Log.d(TAG,"onSubscribe thread:"+Thread.currentThread().getName());
}
#Override
public void onNext(Object value) {
Log.d(TAG,"on next, valor:<<"+value.toString()+">> \n nombre hilo:"+Thread.currentThread().getName());
}
#Override
public void onError(Throwable e) {
Log.d(TAG,"error "+e.toString());
}
#Override
public void onComplete() {
Log.d(TAG,"onCompleted thread:"+Thread.currentThread().getName());
}
};
Plz, let me know if this works for you.
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