I need to download a long list of 30k airports and put it on a offline database.
I made this code to download the json from the web:
bFetch.setOnClickListener(new View.OnClickListener() {
public void onClick(View v)
{
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.baseUrl(GithubService.SERVICE_ENDPOINT).build();
GithubService service = retrofit.create(GithubService.class);
service.getAirport()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Airport>>() {
#Override
public void onCompleted()
{
bClear.setText("OK");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Airport> airports)
{
Log.d("msh",String.valueOf(airports.size()));
}
});
}
});
and it works very well, but if I want to extract only one object, like map or a flatMap, it gives me this:
service.getAirport()
.map(new Func1<List<Airport>, Airport>()
{
#Override
public Airport call(List<Airport> airports) {
return null;
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Subscriber<List<Airport>>() {
#Override
public void onCompleted()
{
bClear.setText("OK");
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Airport> airports)
{
Log.d("msh",String.valueOf(airports.size()));
}
});
}
});
with the error:
Cannot resolve method 'subscribe(anonymous
rx.Subscriber>)
so:
what I have to do to solve it? My problem is that I don't understand very well rX and I have also a bit confusion
could I put data in realm database in map() method (if it works)?
Thank you
Since you're mapping from a List<Airport> to an Airport, you need to have a Subscriber<Airport> instead of Subscriber<List<Airport>>, along with the same change to the onNext method.
looks like it would compile with Java8 and RxJava2-RC5. I changed subscriber param from List to X and the onNext method from List to X. Maybe you coulde provide some more intel on your environment. Please notice that returning null is not possible anymore in RxJava2.
Furthermore notice that using newThread-Scheduler is not a good idea.
This scheduler simply starts a new thread every time it is requested
via subscri beOn() or observeOn() . newThread() is hardly ever a good
choice, not only because of the latency involved when starting a
thread, but also because this thread is not reused. --Tomasz Nurkiewicz from "Reactive Programming with RxJava"
Example-Impl with RxJava2-RC5
Observable.just(Arrays.asList("1", "2", "3"))
.map(new Function<List<String>, String>() {
#Override
public String apply(List<String> s) throws Exception {
return null;
}
}).subscribeOn(Schedulers.newThread())
.subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String value) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Related
I am a beginner for rxjava,room and mvvm architecture.I am trying to fetch a user from room database, using Rxjava.
when I get user successfully I want to show a toast and start another activity. and in case of failure, I will show an error message in text input layout.
I have try to do that using following code.
in my Activity, I have a method authorizeUser() which is called on button click.
private void authorizeUser() {
loginViewModel.checkInDb()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new CompletableObserver() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onComplete() {
Log.e("Action", "Complete");
showToast();
startAnotherActivity();
}
#Override
public void onError(Throwable e) {
Log.e("Action", "error");
showError();
}
});
}
In my ViewModel
public Completable checkInDb() {
Completable completable= Completable.fromAction(() ->
userDataSource.getSingleRecordFromName(name.get(), password.get())
.subscribe(new SingleObserver<User>() {
#Override
public void onSubscribe(Disposable d) {
isLoading.set(true);
}
#Override
public void onSuccess(User user) {
preference.save(Constants.CURRENT_USER, user)
.subscribe();
isLoading.set(false);
isComplete=true;
Log.e("got","success");
}
#Override
public void onError(Throwable e) {
isLoading.set(false);
passwordError.postValue(new Error("Username or password is incorrect"));
Log.e("got","failure");
isComplete=false;
}
}));
return completable;
}
in UserDataSource class
public Single<User> getSingleRecordFromName(String strName) {
return daoAccess.getSingleRecord(strName);
}
and in DaoAccess class
public Single<User> getSingleRecordFromName(String strName) {
return daoAccess.getSingleRecord(strName);
}
Using above code, onComplete method is always called in activity, weather in view model, user fetched successfully or not
But what I want to do is, I want to throw an error or send a notification to the activity when there is error thrown (or on Error method is called in viewmodel). so that I can display error on my activity.
May be my question can be silly, but I am new to this. Please help me.
Although it would be better to maybe wrap the response in LiveData so you would get the subscription/lifecycle "for free", unless there is a specific need to have a completable on the UI, which I don't really any reason for it).
I would instead change your Dao to return Observable instead of Single, and then (one again this is not the optimal solution, should wrap into LiveData) you can return that Observable to the UI:
public Observable checkInDb() {
return userDataSource.getSingleRecordFromName(name.get(), password.get())
.subscribe(new SingleObserver<User>() {
#Override
public void onSubscribe(Disposable d) {
isLoading.set(true);
}
#Override
public void onSuccess(User user) {
preference.save(Constants.CURRENT_USER, user)
.subscribe();
isLoading.set(false);
isComplete=true;
Log.e("got","success");
}
#Override
public void onError(Throwable e) {
isLoading.set(false);
passwordError.postValue(new Error("Username or password is incorrect"));
Log.e("got","failure");
isComplete=false;
}
}));
}
And change your Daos
in UserDataSource class
public Observable<User> getSingleRecordFromName(String strName) {
return daoAccess.getSingleRecord(strName);
}
and in DaoAccess class
public Observable<User> getSingleRecordFromName(String strName) {
return daoAccess.getSingleRecord(strName);
}
So now your view can checkInDb() and handle these cases.
PS: I'm assuming this daoAccess is not an API call rather a local DB (probably Room).
I'm still new toRxJava and I am a bit confused on some code I have, where I don't have direct access to Observables, but pass an Observer as a parameter of some method to execute an Observable.
I would like to combine two Observers in some way but can't figure out how to do it with Observers.
Here I want to "combine" the mGetPotatoes and mGetBurger to show the potatoes and burger only when both do onNext (would be zip() for Observables I guess).
Show me some code
mGetPotatoes.execute( new DisposableObserver<List<Potatoes>>() {
#Override
public void onNext(List<Potatoes> potatoes) {
getMvpView().showPotatoes(mPotatoesMapper.mapPotatoesToViewModels(potatoes));
}
#Override
public void onComplete() {
}
#Override
public void onError(Throwable e) {
getMvpView().hideProgress();
getMvpView().showErrorMessage(e.getMessage());
}
});
mGetBurger.execute( new DisposableObserver<Burger>() {
#Override
public void onNext(Burger burger) {
getMvpView().showBurger(mBurgerMapper.mapBurgerToViewModel(burger));
}
#Override
public void onComplete() {
}
#Override
public void onError(Throwable e) {
getMvpView().hideProgress();
getMvpView().showErrorMessage(e.getMessage());
}
});
Edit
This question is a subset of this one where the template code for the mGetPotatoes and mGetBurger (that are UseCases) is.
I would sugegst you to change your architecture so theObservable is returned and you can manipulate it (change the scheduler, perform some actions, etc)
In case that option is not feasible you may get a workaround with Subjects. A Subject is an Observer which is also an Observable.
For your case:
Create a couple of PublishSubject
Combine both (with the zip) operator and subscribe to the result
Pass both subjects to your original use cases
Not sure about what framework this is, but you can turn back the execute calls into Observables by wrapping them:
Observable<List<Potatoes>> obs = new Observable<List<Potatoes>>() {
#Override public void subscribeActual(final Observer<? super List<Potatoes>> s) {
mGetPotatoes.execute(new DisposableObserver<List<Potatoes>>() {
#Override
public void onStart() {
s.onSubscribe(this);
}
#Override
public void onNext(List<Potatoes> potatoes) {
s.onNext(potatoes);
}
#Override
public void onComplete() {
s.onComplete();
}
#Override
public void onError(Throwable e) {
s.onError(e);
}
}
}
};
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
I receive data from network ,I implemented that part, but after receiving data from server in other thread, I want to save the data in Data base,which I want to implement not in main thread,so after receiving data where I should call the DB insertion method.Here is my code
Observable<List<PhotoAlbum>> searchResponseObservable =
mService.getAPI().getAlbums().subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
searchResponseObservable.subscribe(new Observer<List<PhotoAlbum>>() {
#Override
public void onCompleted() {
Log.i("test","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("test","onComplete");
}
#Override
public void onNext(List<PhotoAlbum> photoAlbums) {
view().showSearchResult(photoAlbums);
}
});
You can use doOnNext() to insert received data into your DB like this:
Observable<List<PhotoAlbum>> searchResponseObservable = mService.getAPI()
.getAlbums()
.subscribeOn(Schedulers.newThread())
.doOnNext(photoAlbums -> insertIntoDb(photoAlbums))
.observeOn(AndroidSchedulers.mainThread());
searchResponseObservable.subscribe(new Observer<List<PhotoAlbum>>() {
#Override
public void onCompleted() {
Log.i("test","onComplete");
}
#Override
public void onError(Throwable e) {
Log.i("test","onComplete");
}
#Override
public void onNext(List<PhotoAlbum> photoAlbums) {
view().showSearchResult(photoAlbums);
}
});
Use flatMap to save in database and use Schedulers IO (Schedulers.io()) for background process
mService.getAPI().getAlbums().flatMap(new Func1<List<PhotoAlbum>, Observable<GetDriversResponse>>() {
#Override
public Observable<GetDriversResponse> call(GetDriversResponse mResponse) {
return mDatabaseHelper.setMyBus(mResponse);
}
}).subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Action1<GetDriversResponse>() {
#Override
public void call(List<PhotoAlbum> photoAlbums{view().showSearchResult(photoAlbums); }}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
throwable.printTrack
}
}));
I have to execute 3 API calls in the sequence and to do so
I use observable.concatMap(new Func1<>...)
and at the last one I have a subscriber to change activity
However I want to update progressBar in UI thread to let user know that part of task is done.
private void getAllData() {
updateUserTask(
getUserScheduleObservable(
getCurrentUserObservable()));
}
private void updateUserTask(Observable<TaskWrapper> observable) {
wrapObservable(observable)
.subscribe(new Subscriber<TaskWrapper>() {
#Override
public void onNext(TaskWrapper taskWrapper) {
openCurrentFragment();
hideProgressIndicators();
}
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable throwable) {
}
});
}
private Observable<TaskWrapper> getUserScheduleObservable(Observable<ScheduleWrapper> observable) {
return observable.concatMap(
scheduleWrappers1 -> apiManager.getRouteObservable(vehicleDeliveryAreaRiderBundle.getVehicle().getId()));
}
private Observable<ScheduleWrapper> getCurrentUserObservable() {
return apiManager.getUserObservable().concatMap(
user -> apiManager.getCurrentScheduleObservable()
);
}
I think that you are looking for something like this.
public class ExampleUnitTest {
#Test
public void testSample() throws Exception {
Observable<String> first = Observable.just("First");
Observable<String> second = Observable.just("Second");
Observable<String> third = Observable.just("Third");
Observable.concat(first, second, third)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnNext(this::updateProgress)
.subscribe();
}
private void updateProgress(String s) {
System.out.println(String.format("Notify your progress that %s ended", s));
}
}
Just concatenating those observables, you can achieve the expected result.
Hope that it helps.
Best regards.