Paging Library for those which don't have any numeric Id - android

I am actually consuming Triposo's API to show the list of countries. https://www.triposo.com/api/
However, in responses, there is no numeric Id and I am struggling while incrementing page size. What should I perform on such responses to overcome? For instance, Github API is fully suitable for Paging Library since it returns itemId as numeric.
https://api.github.com/users
Example Response
{
"results": [
{
"name": "Benin",
"country_id": "Benin",
"snippet": "A safe and relatively easy country for travellers to visit; birthplace of the Voodoo religion and former home of the Kingdom of Dahomey.",
"parent_id": null,
"score": 3.98127216481287,
"id": "Benin"
}
],
"estimated_total": 30845,
"more": true
}
ItemKeyedDataSource.java
public class ItemKeyedCountryDataSource extends ItemKeyedDataSource<Integer, CountryResult> {
private EndpointHelper endpointHelper;
ItemKeyedCountryDataSource() {
endpointHelper = EndpointHelper.getInstance();
}
#Override
public void loadInitial(#NonNull LoadInitialParams<Integer> params, #NonNull final LoadInitialCallback<CountryResult> callback) {
final List<CountryResult> countryResultList = new ArrayList<>();
endpointHelper.getCountryList(0)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry(1)
.onErrorResumeNext(new Func1<Throwable, Observable<? extends CountryWrapper>>() {
#Override
public Observable<? extends CountryWrapper> call(Throwable throwable) {
return Observable.error(throwable);
}
})
.subscribe(new Subscriber<CountryWrapper>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(CountryWrapper countryWrapper) {
if (countryWrapper.getResults().size() > 0) {
countryResultList.addAll(countryWrapper.getResults());
callback.onResult(countryResultList);
}
}
});
}
#Override
public void loadAfter(#NonNull LoadParams<Integer> params, #NonNull final LoadCallback<CountryResult> callback) {
final List<CountryResult> countryResultList = new ArrayList<>();
endpointHelper.getCountryList(params.requestedLoadSize)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.retry(1)
.onErrorResumeNext(new Func1<Throwable, Observable<? extends CountryWrapper>>() {
#Override
public Observable<? extends CountryWrapper> call(Throwable throwable) {
return Observable.error(throwable);
}
})
.subscribe(new Subscriber<CountryWrapper>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(CountryWrapper countryWrapper) {
if (countryWrapper.getResults().size() > 0) {
countryResultList.addAll(countryWrapper.getResults());
callback.onResult(countryResultList);
}
}
});
}
#Override
public void loadBefore(#NonNull LoadParams<Integer> params, #NonNull LoadCallback<CountryResult> callback) {
//Do nothing
}
#NonNull
#Override
public Integer getKey(#NonNull CountryResult item) {
return ?; // what do I return here?
}
}
ViewModel.java
public class SearchableActivityViewModel extends ViewModel {
private LiveData<PagedList<CountryResult>> countryResult;
SearchableActivityViewModel() {
Executor executor = Executors.newFixedThreadPool(5);
CountryResultDataSourceFactory countryResultDataSourceFactory = new CountryResultDataSourceFactory();
PagedList.Config config = new PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setInitialLoadSizeHint(20) //first load
.setPageSize(21)
.build();
//noinspection unchecked
countryResult = new LivePagedListBuilder<String, CountryResult>(countryResultDataSourceFactory, config)
.setFetchExecutor(executor)
.build();
}
public LiveData<PagedList<CountryResult>> getCountryResult(){
return countryResult;
}
#Override
protected void onCleared() {
super.onCleared();
}
}
Thanks in advance. Hope I am clear.
Best,

Related

Get value from CompositeDisposable RXJava - Android

How can I get return value from bellow code :
CompositeDisposable mCompositeDisposable = new CompositeDisposable();
mCompositeDisposable.add(observableSubject(userName, password)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.doOnSubscribe(new Consumer<Disposable>() {
#Override
public void accept(#NonNull Disposable disposable) throws Exception {
}
})
.doFinally(new Action() {
#Override
public void run() throws Exception {
}
})
.subscribeWith(new DisposableObserver<String>() {
#Override
public void onComplete() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(String res) {
//I need to get value from out of mCompositeDisposable.add(...)
Log.i("LOG", res);
}
}));
And :
private Observable<String> observableSubject(String userName, String password) {
return Observable.defer(() -> {
//Some Code
return Observable.just("Value");
});
}
I resolved my problem with EventBus in this method :
#Override
public void onNext(String res) {
//SomeCode ....
//.... EventBus.getDefault() ....
}

rxJava Observable chain stops after onErrorResumeNext

I've the following chain of observables:
public Observable<Void> ApiCallBackObservable() {
return Observable.fromEmitter(new Action1<AsyncEmitter<Void>>() {
#Override
public void call(final AsyncEmitter<Void> voidAsyncEmitter) {
APICall.setCallBack(new CallBack() {
#Override
public void onResponseReceived() {
voidAsyncEmitter.onNext(null);
voidAsyncEmitter.onCompleted();
}
});
}
}, AsyncEmitter.BackpressureMode.NONE).timeout(100, TimeUnit.MILLISECONDS).onErrorResumeNext(new Func1<Throwable, Observable<? extends Void>>() {
#Override
public Observable<? extends Void> call(Throwable throwable) {
return Observable.empty();
}
});
}
The problem is that after this API callback i'm chaining more observables:
apiCallBackObservable().map(new Func1<Void, String>() {
#Override
public String call(Void aVoid) {
return "Ok";
}
}).flatmap(...).flatmap(....)....;
but somehow when the code hits the onErrorResumeNext(because of the timeout), the chain stops it never hits the next map, no error no anything it just stops.

Adding a Maybe to a CompositDisposable in RxJava2

In my Activity I have the following, which results in the error message 'Error:(190, 35) error: incompatible types: MaybeObserver cannot be converted to Disposable'. I expected this to work because I've been doing something similar with a Completable and a DisposableCompletableObserver combination. How can I use a Maybe with a CompositeDisposable in RxJava2?
private final CompositeDisposable disposables = new CompositeDisposable();
// ...
String id = authManager.getUserID();
Maybe maybe = userManager.getUser(id);
disposables.add(maybe
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new DisposableMaybeObserver() {
#Override
public void onSuccess(Object o) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
}));
You should use ResourceMaybeObserver:
private final CompositeDisposable disposables = new CompositeDisposable();
String id = authManager.getUserID();
Maybe<Object> maybe = userManager.getUser(id);
disposables.add(maybe
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribeWith(new ResourceMaybeObserver<Object>() {
#Override
public void onSuccess(#NonNull Object o) {
}
#Override
public void onError(#NonNull Throwable e) {
}
#Override
public void onComplete() {
}
}));

RxAndroid interval API call. Is there a better way?

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);
}
});

Chaining observables based on result

I am a complete beginner on rx-java and rx-android. I've heard the learning curve is quite steep in the beginning.
Im trying to replace all Eventbus based code to a more typesafe alternative by using rx-android.
I've set up this snippet to create observables from edit text text change events:
MainActivity
RxUtils.createEditTextChangeObservable(txtInput).throttleLast(200, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()).subscribe(new Action1<EditText>() {
#Override
public void call(EditText editText) {
searchStopResultFragment.query(editText.getText().toString());
}
});
RxUtils:
public static Observable<EditText> createEditTextChangeObservable(final EditText editText){
return Observable.create(new Observable.OnSubscribe<EditText>() {
#Override
public void call(final Subscriber<? super EditText> subscriber) {
editText.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
#Override
public void afterTextChanged(Editable s) {
if (subscriber.isUnsubscribed()) return;
subscriber.onNext(editText);
}
});
}
});
}
SearchStopResultFragment:
public void query(String query){
lastQuery = query;
resultObservable = StopProvider.getStopResultObservable(getActivity().getContentResolver(),query);
subscription = resultObservable.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<List<Stop>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(List<Stop> stops) {
if(!lastQuery.equals("")) {
if(stops.size()>0) {
ArrayList<AdapterItem> items = adapter.getItems();
items.clear();
for (Stop stop : stops) {
SearchResultStopItem item = new SearchResultStopItem(stop, SearchResultStopItem.STOP);
items.add(item);
}
adapter.setItems(items);
adapter.notifyDataSetChanged();
}else{
//DO A NOTHER ASYNC QUERY TO FETCH RESULTS
}
}else{
showStartItems();
}
}
});
}
It feels like i'm doing this wrong. I create new observables from the query method in my fragment on every text change event. I also want to create a new async lookup operation based off the result in StopProvider.getStopResultObservable (see the comment)
Any thoughs?
Here is what I came up with:
RxUtils.createEditTextChangeObservable(txtInput)
.throttleLast(200, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.map(EXTRACT_STRING)
.filter(STRING_IS_NOT_EMPTY)
.concatMap(new Func1<EditText, Observable<Pair<String,List<Stop>>>>() {
#Override
public Observable<Pair<String, List<Stop>>> call(final String query) {
return StopProvider.getStopResultObservable(getContentResolver(), query)
.map(new Func1<List<Stop>, Pair<String, List<Stop>>>() {
// I think this map is a bit more readable than the
// combineLatest, and since "query" should not be changing
// anyway, the result should be the same (you have to
// declare it as final in the method signature, though
#Override
public Pair<String, List<Stop>> call(List<Stop> stops) {
return new Pair(query, stops);
}
});
}
)
.concatMap(new Func1<Pair<String, List<Stop>>, Observable<List<Stop>>>() {
#Override
public Observable<List<Stop>> call(Pair<String, List<Stop>> queryAndStops) {
if (queryAndStops.second.size() == 0) {
return RestClient.service().locationName(queryAndStops.first)
.map(new Func1<LocationNameResponse, List<Stop>>() {
#Override
public List<Stop> call(LocationNameResponse locationNameResponse) {
// since there was no if-else in your original code (you were always
// just wrapping the List in an Observable) I removed that, too
return locationNameResponse.getAddresses();
}
});
} else {
return Observable.just(queryAndStops.second);
}
}
)
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.compose(this.<List<Stop>>bindToLifecycle())
.subscribe(new Action1<List<Stop>>() {
#Override
public void call(List<Stop> stops) {
// since I don't know what your API is returning I think
// it's saver to keep this check in:
if (stops != null) {
searchStopResultFragment.showStops(stops);
} else {
searchStopResultFragment.showStartItems();
}
}
},
new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
showError(throwable);
}
});
where:
public static final Func1<EditText, String> EXTRACT_STRING = new Func1<EditText, String>() {
#Override
public void String call(EditText editText) {
return editText.getText().toString();
}
};
public static final Func1<String, Boolean> STRING_IS_NOT_EMPTY = new Func1<String, Boolean>() {
#Override
public void String call(String string) {
return !string.isEmpty();
}
};
So, this at least removes the need to return Observable.just(null) and then check for that down the chain.
You can move your second concatMap to the only place you need it - after combineLatest
RxUtils.createEditTextChangeObservable(txtInput)
.throttleLast(200, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread())
.concatMap(new Func1<EditText, Observable<Pair<String, List<Stop>>>>() {
#Override
public Observable<Pair<String, List<Stop>>> call(EditText editText) {
String query = editText.getText().toString();
//searchStopResultFragment.setLastQuery(query);
if (query.isEmpty()) {
return Observable.just(null);
}
return Observable
.combineLatest(StopProvider.getStopResultObservable(getContentResolver(), query), Observable.just(query), new Func2<List<Stop>, String, Pair<String, List<Stop>>>() {
#Override
public Pair<String, List<Stop>> call(List<Stop> stops, String s) {
return new Pair(s, stops);
}
})
.concatMap(new Func1<R, Observable<? extends Pair<String, List<Stop>>>>() {
#Override
public Observable<? extends Pair<String, List<Stop>>> call(R r) {
if (queryAndStops.second.size() == 0) {
return RestClient.service().locationName(queryAndStops.first).concatMap(new Func1<LocationNameResponse, Observable<? extends List<Stop>>>() {
#Override
public Observable<? extends List<Stop>> call(LocationNameResponse locationNameResponse) {
return Observable.just(locationNameResponse.getAddresses());
}
});
} else {
return Observable.just(queryAndStops.second);
}
}
});
}
})
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()).compose(this.<List<Stop>>bindToLifecycle())
.subscribe(new Action1<List<Stop>>() {
#Override
public void call(List<Stop> stops) {
if (stops != null) {
searchStopResultFragment.showStops(stops);
} else {
searchStopResultFragment.showStartItems();
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
showError(throwable);
}
});
Solved it using concatmap and combine latest:
RxUtils.createEditTextChangeObservable(txtInput).throttleLast(200, TimeUnit.MILLISECONDS, AndroidSchedulers.mainThread()).concatMap(new Func1<EditText, Observable<Pair<String,List<Stop>>>>() {
#Override
public Observable<Pair<String, List<Stop>>> call(EditText editText) {
String query = editText.getText().toString();
//searchStopResultFragment.setLastQuery(query);
if(query.isEmpty()){
return Observable.just(null);
}
return Observable.combineLatest(StopProvider.getStopResultObservable(getContentResolver(), query), Observable.just(query), new Func2<List<Stop>, String, Pair<String, List<Stop>>>() {
#Override
public Pair<String, List<Stop>> call(List<Stop> stops, String s) {
return new Pair(s,stops);
}
});
}
}).concatMap(new Func1<Pair<String, List<Stop>>, Observable<List<Stop>>>() {
#Override
public Observable<List<Stop>> call(Pair<String, List<Stop>> queryAndStops) {
if(queryAndStops!=null) {
if (queryAndStops.second.size() == 0) {
return RestClient.service().locationName(queryAndStops.first).concatMap(new Func1<LocationNameResponse, Observable<? extends List<Stop>>>() {
#Override
public Observable<? extends List<Stop>> call(LocationNameResponse locationNameResponse) {
return Observable.just(locationNameResponse.getAddresses());
}
});
} else {
return Observable.just(queryAndStops.second);
}
}
return Observable.just(null);
}
}).subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread()).compose(this.<List<Stop>>bindToLifecycle()).subscribe(new Action1<List<Stop>>() {
#Override
public void call(List<Stop> stops) {
if (stops != null) {
searchStopResultFragment.showStops(stops);
}else{
searchStopResultFragment.showStartItems();
}
}
}, new Action1<Throwable>() {
#Override
public void call(Throwable throwable) {
showError(throwable);
}
});
However is there some nicer way to break out of the chain without sending Observable.just(null) and check for nulls in next call?

Categories

Resources