I created an Observable using the Observable.fromCallable method and subscribed to it as shown in the code snippet below.
Observable<String> stringObservable = Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(1000);
return Thread.currentThread().getName();
}
});
stringObservable.subscribeOn(Schedulers.io());
stringObservable.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String aDouble) {
Toast.makeText(SimpleActivity.this, "onNext: " + aDouble,
Toast.LENGTH_LONG).show();
}
#Override
public void onError(Throwable e) {
new AlertDialog.Builder(SimpleActivity.this)
.setTitle("Error")
.setMessage(e.toString())
.show();
}
#Override
public void onComplete() {
}
});
The snippet above produced a toast showing that the Callable was run on the main thread instead of the Schedulers.io thread. What's happening?
Every operation on an Observable creates a new instance and does not effect the original one. Therefore
stringObservable.subscribeOn(Schedulers.io());
does not affect your code below.
The correct way of using them would be in a chain instead of using a variable.
Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
Thread.sleep(1000);
return Thread.currentThread().getName();
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String aDouble) {
Toast.makeText(SimpleActivity.this, "onNext: " + aDouble,
Toast.LENGTH_LONG).show();
}
#Override
public void onError(Throwable e) {
new AlertDialog.Builder(SimpleActivity.this)
.setTitle("Error")
.setMessage(e.toString())
.show();
}
#Override
public void onComplete() {
}
});
Related
I make n asynchronous calls (n being the size of the arraylist, and indexes passed as integer parameters to the calls) and want to invoke a method when all of the calls are completed. I implemented the following code below. I used a counter to know that all of the calls are completed. It is working, however I know that it could be done in a more efficient and elegant way.
int n = mUserUrls.getM3u().size();
counter = n;
Observable.range(0, n)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(new Consumer<Integer>() {
#Override
public void accept(Integer integer) throws Exception {
final int index = integer;
Single<ResponseBody> singleGetChannels = aPI.getChannels(mUserUrls.getM3u().get(integer))
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Single<List<EPG>> singleGetEPGs = aPI.getEPGs(mUserUrls.getJson())
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
Single.zip(singleGetChannels, singleGetEPGs, new BiFunction<ResponseBody, List<EPG>, ChannelsAndEPG>() {
#Override
public ChannelsAndEPG apply(ResponseBody responseBodyChannels, List<EPG> ePGs) {
ChannelsAndEPG channelsAndEPG = new ChannelsAndEPG(responseBodyChannels, ePGs);
return channelsAndEPG;
}
}).subscribe(new SingleObserver<ChannelsAndEPG>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onSuccess(ChannelsAndEPG channelsAndEPG) {
m3Us.put(index, M3UParser.parseList(channelsAndEPG.mResponseBodyChannels.byteStream()));
setEPGs(index, channelsAndEPG.mEPG);
setEPGsForNext24Hours();
counter--;
if (counter == 0) {
if (mCallback != null) {
isDataLoaded = true;
mCallback.onDataLoaded();
}
}
}
#Override
public void onError(Throwable e) {
}
});
}
})
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Integer integer) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Log.i(TAG, "onComplete called");
}
});
You can use flatMap to convert each integer to Single ( same way you're doing it now). And then call toList to get Single.
You could use this :
Observable.fromIterable(mUserUrls.getM3u())
.flatMap{ m3u ->
aPI.getChannels(m3u.getInteger)
.zipWith(aPI.getEPGs(mUserUrls.getJson()))
.subscribeOn(Schedulers.io())
}
.doOnNext{
m3Us.put(index, M3UParser.parseList(channelsAndEPG.mResponseBodyChannels.byteStream()));
setEPGs(index, channelsAndEPG.mEPG);
setEPGsForNext24Hours();
}
.subscribe(new Observer<Integer>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(Integer integer) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
Log.i(TAG, "onComplete called");
}
})
While inflating Android view I load a bunch of stuff from the background thread and inflate some views based on network responses. So I am trying to defer some of that tasks using RxJava like this
Single.fromCallable(() -> savedInstanceState)
.delay(50,TimeUnit.MICROSECONDS,AndroidSchedulers.mainThread())
.flatMapCompletable(this::loadVideos)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
Timber.d("on Subscribe");
}
#Override
public void onComplete() {
Timber.d("on onComplete");
}
#Override
public void onError(Throwable e) {
Timber.d("on onError");
}
});
And the loadVideos method is like this:
private Completable loadVideos(Bundle savedInstanceState) {
return Completable.fromAction(() -> {
videoPresenter.loadVideos(savedInstance);
});
}
What I am finding is onSubscribe() certainly gets called, but method videoPresenter.loadVideos never gets called. Would appreciate if anyone can point out what I am doing wrong.
For my testing, I implemented following test that seems to work...
public class DelayTest {
public static void main(String[] args) throws InterruptedException {
Single.fromCallable(() -> "hello")
.delay(50, TimeUnit.MICROSECONDS)
.flatMapCompletable(new Function<String, CompletableSource>() {
#Override
public CompletableSource apply(String s) throws Exception {
return getFlatMapCompletable();
}
})
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
System.out.println("In onSubscribe");
}
#Override
public void onComplete() {
System.out.println("In onComplete");
}
#Override
public void onError(Throwable e) {
System.out.println("In onError");
}
});
Thread.sleep(200L);
}
private static Completable getFlatMapCompletable() {
return Completable.fromAction(new Action() {
#Override
public void run() throws Exception {
System.out.println("In flatmapCompletable");
}
});
}
}
Delay operator in RxJava is executed in another thread. So the rest of the execution does not wait for this one to be finished.
Take a look to some examples https://github.com/politrons/reactive/blob/master/src/test/java/rx/observables/utils/ObservableDelay.java
I am trying to fetch list from an api and display in recyclerview using Retrofit with RxJava.
I have used below code-
ApiInterface apiService =
ApiService.getClient().create(ApiInterface.class);
Observable<MoviesResponse> call = apiService.getTopRatedMovies(API_KEY);
call.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
call.subscribe(new Observer<MoviesResponse>() {
#Override
public void onSubscribe(Disposable d) {
Toast.makeText(getApplicationContext(), d.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onNext(MoviesResponse moviesResponse) {
movies=moviesResponse;
moviesAdapter.notifyDataSetChanged();
}
#Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onComplete() {
Toast.makeText(getApplicationContext(), "complete", Toast.LENGTH_LONG).show();
}
});
Below two lines specify that the REST call will be made in a new thread . And when the call response returns, the onNext, onError, and onComplete methods are called on the mainThread.
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
But I am getting NetworkOnMainThreadException which is thrown when an application attempts to perform a networking operation on its main thread.Why am i getting this exception and how can i resolve this?
That's because you are subscribing to the observable 2 times. You can remove subscribe method from here:
call.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe();
So it will look like this:
ApiInterface apiService =
ApiService.getClient().create(ApiInterface.class);
call.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MoviesResponse>() {
#Override
public void onSubscribe(Disposable d) {
Toast.makeText(getApplicationContext(), d.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onNext(MoviesResponse moviesResponse) {
movies=moviesResponse;
moviesAdapter.notifyDataSetChanged();
}
#Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onComplete() {
Toast.makeText(getApplicationContext(), "complete", Toast.LENGTH_LONG).show();
}
});
You are subscribing twice, the second time to the unmodified call source. You should have something like this:
ApiInterface apiService =
ApiService.getClient().create(ApiInterface.class);
Observable<MoviesResponse> call = apiService.getTopRatedMovies(API_KEY);
call.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<MoviesResponse>() {
#Override
public void onSubscribe(Disposable d) {
Toast.makeText(getApplicationContext(), d.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onNext(MoviesResponse moviesResponse) {
movies=moviesResponse;
moviesAdapter.notifyDataSetChanged();
}
#Override
public void onError(Throwable e) {
Toast.makeText(getApplicationContext(), e.toString(), Toast.LENGTH_LONG).show();
}
#Override
public void onComplete() {
Toast.makeText(getApplicationContext(), "complete", Toast.LENGTH_LONG).show();
}
});
I use retrofit2 in my project. I have interface:
public interface ProductService {
#POST("findProducts")
Observable<ProductsResponse> getProducts();
}
and service method:
public ProductService getProductService() {
return getService(ProductService.class);
}
After that I use this service in my fragment. When I click to button I call this method:
#Override
public void onClick(View view) {
if (view.getId() == R.id.button) {
RestApiFactory.getInstance().getProductService().getProducts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new ProductHandler());
}
}
and realise handler in this fragment:
private class ProductHandler implements Observer<ProductsResponse> {
#Override
public void onSubscribe(Disposable d) {
Log.d(TAG, "onSubscribe: start");
progressBar.setVisibility(View.VISIBLE);
}
#Override
public void onNext(ProductsResponse value) {
//save to DB in new Thread
}
#Override
public void onError(Throwable e) {
Log.d(TAG, "onError: "+ e.getMessage());
price.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.INVISIBLE);
}
#Override
public void onComplete() {
Log.d(TAG, "onComplete: finish");
price.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.INVISIBLE);
}
}
is it correct to implement ProductHandler implements Observer<ProductsResponse>'s methods
onSubscribe
onNext
onError
onComplete
in fragment?
I'm advised to move this in a singleton:
MySingleton.getInstanse().getData();
and move all to getData :
public void getData(){
RestApiFactory.getInstance().getProductService().getProducts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<ProductsResponse>() {
#Override
public void onSubscribe(Disposable d) {
//send broadcast and catch in Fragment
}
#Override
public void onNext(ProductsResponse productsResponse) {
//send broadcast and catch in Fragment
}
#Override
public void onError(Throwable e) {
//send broadcast and catch in Fragment
}
#Override
public void onComplete() {
//send broadcast and catch in Fragment
}
});
}
but I think this is nonsense. who will tell you how to use it (in what place) Observable from retrofit?
there is no need to implement Observer.You can just need to create an observer for the same.Let's see the example.
service.getProducts()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(mObserver);
}
the observer should look like and must be initialized in oncreate:
mObserver = new Observer<ProductsResponse>() {
#Override
public void onSubscribe(Disposable d) {
mDisposableList.add(d);
}
#Override
public void onNext(ProductsResponse value) {
#Override
public void onError(Throwable e) {
e.printStackTrace();
}
#Override
public void onComplete() {
}
};
Am doing a weather API call every 3secs and append the result to a textView using showWeather("weather") but I am sure there is a better way to do it. Am not sure why I need create Class Func1 but did because map required it. Also is there a way to shorten observer? I don't use lamda unfortunately. Any suggestions?
Observer myObserver = new Observer<String>() {
#Override
public void onError(Throwable e) {
Logger.getLogger(getClass().getName()).log(Level.SEVERE, null, e);
}
#Override
public void onComplete() {
}
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String value) {
showWeather(value);
}
};
class Func1<T, T1> implements io.reactivex.functions.Function<Long, String > {
#Override
public String apply(Long aLong) throws Exception {
return getJSON("http://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b1b15e88fa797225412429c1c50c122a1",300);
}
}
Observable.interval(3, TimeUnit.SECONDS)
.map(new Func1<Long, Observable<String>>() {
}).observeOn(AndroidSchedulers.mainThread()).subscribe(myObserver);
I tried :
Observable
.interval(3, SECONDS)
.subscribeOn(Schedulers.io())
.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
return getJSON("http://samples.openweathermap.org/data/2.5/weather?q=London,uk&appid=b1b15e88fa797225412429c1c50c122a1", 300);
}
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(myObserver)
But I get :
03-07 21:47:25.982 21181-21181/com.alex.rxandroidexamples E/imerExampleFragment$1$1: null
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1273)
Also how do I unsubscribe onPause or onPause?
I found out the best way to do this is :
private final CompositeDisposable disposables = new CompositeDisposable();
Observable fetchWeatherInterval = Observable.interval(3, TimeUnit.SECONDS)
.map(new Function<Long, String>() {
#Override
public String apply(Long aLong) throws Exception {
return getWeather("http://samples.openweathermap.org/data/2.5/weather?", "London,uk", "b1b15e88fa797225412429c1c50c122a1");
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread());
Observer displayWeatherInterval = new Observer<String>() {
#Override
public void onError(Throwable e) {
Log.e("Throwable ERROR", e.getMessage());
}
#Override
public void onComplete() {
}
#Override
public void onSubscribe(Disposable d) {
disposables.add(d);
}
#Override
public void onNext(String value) {
textViewWeatherInterval.append(value);
}
};
buttonFetchIntervalWeather.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
fetchWeatherInterval.subscribe(displayWeatherInterval);
}
});