I have a method that downloads api data. But I get response with empty data, so I need to check if data is correct.
Call the method:
try {
fetchChartData();
} catch (EmptyResponseException e) {
view.showError();
}
And fetch data:
public void fetchChartData() throws EmptyResponseException {
Call<ChartModel> dailyChartCall = ... //some call
dailyChartCall.enqueue(new Callback<ChartModel>() {
#Override
public void onResponse(Call<ChartModel> call, Response<ChartModel> response) {
//EMPTY
if(response.body().getData().size() == 0){
<<-- THROW HERE
} else ...
How should I implement exception and pass it to the top?
Simple 'throw new EmptyResponseException' is not working as onResponse is anonymous class.
I assume that you are designing your app using MVP structure.
So instead of catching exception, you can do
public void fetchChartData(){
Call<ChartModel> dailyChartCall = ... //some call
dailyChartCall.enqueue(new Callback<ChartModel>() {
#Override
public void onResponse(Call<ChartModel> call, Response<ChartModel> response) {
//EMPTY
if(response.body().getData().size() == 0){
***presenter.showError();***
} else ...
{
>>>> do your logic here <<<<
presenter.showChartData();
}
Which in presenter showError and showChartData you can call corresponding view function.
Related
In MainActivityViewModel class i have one Getter method that returns an instance of CurrentWeather (pojo class) and this method needs response from OnResponse method but I get null for first time.
The first methods invoke from MainActivity, viewModel is not null but the currentWeather instance is.
MainActivityViewModel viewModel = ViewModelProviders.of(this).get(MainActivityViewModel.class);
currentWeather = viewModel.getCurrentWeather();
I don't know if I can ask to wait for a moment before return currentWeather in first method or not.
public class MainActivityViewModel extends ViewModel implements Callback<ResponseBody> {
private CurrentWeather currentWeather;
public CurrentWeather getCurrentWeather() {
if (currentWeather == null) {
createCurrentWeather("London");
}
return currentWeather;
}
public void createCurrentWeather(String city) {
RetrofitApiManager.getInstance().getCurrentWeatherApi(this, city);
}
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
ResponseBody body = response.body();
try {
String serverResponde = body.string();
Timber.e(serverResponde);
Gson gson = new Gson();
currentWeather = gson.fromJson(serverResponde, CurrentWeather.class);
} catch (IOException e) {
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
}
It's because it takes a while before a response is returned.
Usually, you need a LiveData object to get results from background tasks.
In your MainActivityViewModel, add the following:
private MutableLiveData currentWeatherData = new MutableLiveData<CurrentWeather>();
public LiveData<CurrentWeather> getCurrentWeatherData() {
return currentWeatherData;
}
When you get response, update your LiveData
currentWeather = gson.fromJson(serverResponde, CurrentWeather.class);
currentWeatherData.postValue(currentWeather);
In your activity, you need to observe this LiveData.
viewModel.getCurrentWeatherData().observe(this, new Observer<CurrentWeather>() {
#Override
public void onChanged(CurrentWeather c) {
// Do whatever you want with c.
}
});
I'm new in RxJava. I have currently executed three API calls parallel which is independent of each other via Retrofit using Single.Zip Operator. On getting a successful response of all three API calls, I have to insert the data from all three APIs into Room database into Different entities which takes 20 seconds.
So I need to execute database operations inside Single.Zip operator. Because the logic is written inside onSuccess method running away before Database Operation performed.
I have tried to take separate Observer for performing database operation but didn't work.
public void callOfflineDataAPIs() {
setIsLoading(true);
Single<BaseResponse<ProductResponse>> single1 = getDataManager().getOfflineProductListApiCall(getDataManager().getLastTimeStampOfflineProductCall()).subscribeOn(getSchedulerProvider().io()).observeOn(getSchedulerProvider().ui());
Single<BaseResponse<LocationResponse>> single2 = getDataManager().getOfflineLocationListApiCall(getDataManager().getLastTimeStampOfflineLocationCall()).subscribeOn(getSchedulerProvider().io()).observeOn(getSchedulerProvider().ui());
Single<BaseResponse<OfflineMasterData>> single3 = getDataManager().getOfflineMasterDataListApiCall(getDataManager().getLastTimeStampOfflineMasterCall()).subscribeOn(getSchedulerProvider().io()).observeOn(getSchedulerProvider().ui());
DisposableSingleObserver<List<Boolean>> result = Single.zip(single3, single1, single2,
(offlineMasterDataBaseResponse, productResponseBaseResponse, locationResponseBaseResponse) -> {
List<Boolean> apiCalls = new ArrayList<>();
apiCalls.add(masterDataCRUDOperation(offlineMasterDataBaseResponse));
apiCalls.add(productDataCRUDOperation(productResponseBaseResponse));
apiCalls.add(locationDataCRUDOperation(locationResponseBaseResponse));
return apiCalls;
}).subscribeOn(getSchedulerProvider().io()).observeOn(getSchedulerProvider().ui()).subscribeWith(new DisposableSingleObserver<List<Boolean>>() {
#Override
public void onSuccess(List<Boolean> apiCalls) {
setIsLoading(false);
LogHelper.e(TAG, "DisposableSingleObserver- onSuccess");
boolean isSync = true;
for (int i = 0; i < apiCalls.size(); i++) {
if (!apiCalls.get(i)) {
isSync = false;
LogHelper.e(TAG, "DisposableSingleObserver- onSuccess- apiCalls.get(i)", i);
callOfflineDataAPIs();
break;
}
}
if (isSync) {
LogHelper.e(TAG, "IF-isSync");
if (BuildConfig.IS_CLIENT_BUILD) {
LogHelper.e(TAG, "IF-isSync-IS_CLIENT_BUILD-true");
getDataManager().setCurrentWarehouseKey(1);
getNavigator().onGoButtonClick();
} else {
LogHelper.e(TAG, "ELSE-isSync-IS_CLIENT_BUILD-false");
getWarehouseList();
}
}
}
#Override
public void onError(Throwable e) {
LogHelper.e(TAG, "DisposableSingleObserver- Throwable");
setIsLoading(false);
String errorMessage = new NetworkError(e).getAppErrorMessage();
getNavigator().exitApplicationOnError(errorMessage);
}
});
}
Logic written inside onSuccess Method execute once all DB Operation performed.
You can modify your code to something like:
DisposableSingleObserver<List<Boolean>> result = Single.zip(single3, single1, single2,
(offlineMasterDataBaseResponse, productResponseBaseResponse, locationResponseBaseResponse) -> {
List<Boolean> apiCalls = new ArrayList<>();
apiCalls.add(masterDataCRUDOperation(offlineMasterDataBaseResponse));
apiCalls.add(productDataCRUDOperation(productResponseBaseResponse));
apiCalls.add(locationDataCRUDOperation(locationResponseBaseResponse));
return apiCalls;
}).subscribeOn(getSchedulerProvider().io())
.map(new Function<List<Boolean> apiCalls, List<Boolean> apiCalls>() {
#Override
public List<Boolean> apiCalls apply(List<Boolean> apiCalls) throws Exception {
// perform database operations here
return apiCalls;
}
})
.observeOn(getSchedulerProvider().ui())
.subscribe(new Observer<List<Boolean>>() {
#Override
public void onNext(User user) {
// Do something
}
#Override
public void onError(Throwable e) {
// Do something
}
#Override
public void onComplete() {
// Do something
}
});
I'm new to room & livedata. My task is to get the data through service and insert into room database and update the UI through livedata observer but whenever I'm doing this task, observer is calling for 3 times because of livedata changes(insert, update or delete). But actually what I need is to call observer only once after completing all the queries (insert/update/delete), then remove the observers from main thread. Please anyone help to resolve
Observer initialization from main fragment :
int updateCount=0; // i don't like to use this count
public void configureViewModel(){
SCREEN_ID=SCREEN_NO_ADDRESS_DETAIL;
viewModel = ViewModelProviders.of(this, viewModelFactory).get(DynamicUIViewModel.class);
viewModel.init(SCREEN_ID,loanType);
Observer observer=new Observer() {
#Override
public void onChanged(#Nullable Object o) {
List<DynamicUITable> list=(List<DynamicUITable>)o;
updateCount++;
if(updateCount==3) {
viewModel.getDynamicUITableLiveData().removeObserver(this); // i want to remove observer without checking this updateCount condition.
updateCount=0;
updateUI(list);
}
}
};
viewModel.getDynamicUITableLiveData().observe(getViewLifecycleOwner(), observer);
}
Repository Class :
public LiveData<List<DynamicUITable>> init(String screenName,String loanType){
try {
refreshData(screenName,loanType);
}catch (Exception ex){
ex.printStackTrace();
}
return dynamicUIDao.load(screenName);
}
Service call method :
private void refreshData(final String screenName,String loanType){
try{
executor.execute(()->{
boolean dataExist = (dynamicUIDao.getTableBasedOnScreen(screenName) != null);
dynamicUIWebservice.getDynamicUIFromServer(screenName).enqueue(new Callback<List<DynamicUITable>>() {
#Override
public void onResponse(Call<List<DynamicUITable>> call, Response<List<DynamicUITable>> response) {
Log.e("TAG", "DATA REFRESHED FROM NETWORK");
executor.execute(()-> {
List<DynamicUITable> dynamicUITableList = response.body();
if (dynamicUITableList != null) {
if (dataExist) {
dynamicUIDao.deleteRecords(screenName);
dynamicUIDao.save(dynamicUITableList);
} else {
dynamicUIDao.save(dynamicUITableList);
dynamicUIDao.load(screenName);
}
/* From here observer is calling whenever live data changes but what i need is , i need to
call observer only one time after completion all query operation*/
}
});
}
#Override
public void onFailure(Call<List<DynamicUITable>> call, Throwable t) {
}
});
});
}catch (Exception ex){
ex.printStackTrace();
}
}
You could run all the db operations in one Transaction.
This will cause your observers to be notified once since the insert, replace and delete will be done in a single transaction.
I use retrofit2 with rxjava extension.
I have a list of REST API urls and want to do this:
for each
check whether a corresponding file locally exists
if yes: call the API and store the response or the HTTP error
if not: store a customized error
return the list of those results
My problem is: apply returns (with an empty RequestResult) before the server response is received. I think, I understand why, but I don't know how to fix it, because I need to return a RequestResult and not the Retrofit observable.
How can this be solved?
Here is my code:
#GET
Observable<Response<ResponseBody>> enroll(#Url String url);
class RequestResult {
CustomException error;
Response<ResponseBody> response;
}
Observable<ClassOfListItem> observable = Observable.fromIterable(listOfItems);
observable
.flatMap(new Function<ClassOfListItem, ObservableSource<RequestResult>>() {
#Override
public ObservableSource<RequestResult> apply(ClassOfListItem listItem) throws Exception {
RequestResult requestResult = new RequestResult();
if (fileExists(listItem.url)) {
Observable<Response<ResponseBody>> callObservable = restAPI.enroll(listItem.url)
.subscribeOn(Schedulers.io());
callObservable
.subscribe(new DisposableObserver<Response<ResponseBody>>() {
#Override
public void onNext(Response<ResponseBody> responseBodyResponse) {
onPremiseEnrollmentResult.response = responseBodyResponse;
}
#Override
public void onError(Throwable e) {
onPremiseEnrollmentResult.error = new CustomException(e);
}
#Override
public void onComplete() {
}
});
}
else {
requestResult.error = new CustomException("file not found");
}
return Observable.just(requestResult);
}
}
.toList()
.observerOn(AndroidScheduler.mainThread())
.subscribe(new DisposableSingleObserver<List<RequestResult>>() {
#Override
public void onError(Throwable e) {
Log.d("onError", e.getMessage());
}
#Override
public void onSuccess(List<RequestResult> requestResults) {
// parse results
}
}
)
The flatMap() operator allows you to turn one observable into a different observable. You have a nested observer chain inside your apply() which is not part of the observer chain, so it will be empty because it has not completed yet.
To fix this, when the file exists, return the observable.
observable
.flatMap(new Function<ClassOfListItem, ObservableSource<RequestResult>>() {
#Override
public ObservableSource<RequestResult> apply(ClassOfListItem listItem) throws Exception {
RequestResult requestResult = new RequestResult();
if (fileExists(listItem.url)) {
return restAPI.enroll(listItem.url)
.subscribeOn(Schedulers.io());
}
return Observable.error( new CustomException("file not found") );
}
}
.toList()
.observerOn(AndroidScheduler.mainThread())
.subscribe(new DisposableSingleObserver<List<RequestResult>>() {
#Override
public void onError(Throwable e) {
Log.d("onError", e.getMessage());
}
#Override
public void onSuccess(List<RequestResult> requestResults) {
// parse results
}
}
If you need to capture both errors and successes into the list, then you can add map() operator to wrap RequestResult around the response and onErrorResumeNext() to wrap RequestResult around the error before the toList() operator.
If you are making api call on background thread then what you can do is invoke it synchronously....in your case your retrofit api method would change to following
Call<Response<ResponseBody>> enroll(#Url String url);
and you'd invoke by calling restAPI.enroll(listItem.url).execute()
I'm pretty new to RxJava and Retrofit and am trying to write my API calls with it. All the API calls return a JSON body on error which is in the general format as,
{"errors":[{"code":100, "message":"Login/Password not valid", "arguments":null}]}
Currently my code for the login API call (others are also similar) is,
mConnect.login(id, password)
.subscribe(new Subscriber<Token>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted()");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError(): " + e);
if (e instanceof HttpException) {
// dump e.response().errorBody()
}
}
#Override
public void onNext(Token token) {
Log.d(TAG, "onNext(): " + token);
}
});
When I get an error at the onError(), I would like to automatically decode the JSON in the error body to a POJO instead and use that. Is there a way to do this preferably in one place for all other API calls. Any help is appreciated.
I would suggest the use of a reusable Transformer along with the onErrorResumeNext operator to encapsulate your logic. It'd look something like this:
<T> Observable.Transformer<T, T> parseHttpErrors() {
return new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable.onErrorResumeNext(new Func1<Throwable, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Throwable throwable) {
if (throwable instanceof HttpException) {
HttpErrorPojo errorPojo = // deserialize throwable.response().errorBody();
// Here you have two options, one is report this pojo back as error (onError() will be called),
return Observable.error(errorPojo); // in this case HttpErrorPojo would need to inherit from Throwable
// or report this pojo back as part of onNext()
return Observable.just(errorPojo); //in this case HttpErrorPojo would need to inherit from <T>
}
// if not the kind we're interested in, then just report the same error to onError()
return Observable.error(throwable);
}
});
}
};
}
Pay attention to the comments in the code, since you have to make the decision whether you want to report the parsed response onError() or onNext().
Then you can use this transformer anywhere in your API calls like this:
mConnect.login(id, password)
.compose(this.<Token>parseHttpErrors()) // <-- HERE
.subscribe(new Subscriber<Token>() {
#Override
public void onCompleted() {
Log.d(TAG, "onCompleted()");
}
#Override
public void onError(Throwable e) {
Log.e(TAG, "onError(): " + e);
if (e instanceof HttpErrorPojo) {
// this will be called if errorPojo was reported via Observable.error()
}
}
#Override
public void onNext(Token token) {
Log.d(TAG, "onNext(): " + token);
if (token instanceof HttpErrorPojo) {
// this will be called if errorPojo was reported via Observable.just()
}
}
});
Deserialize may be an issue too. You can use the retrofit converter to deserialize it (or do it yourself).
My solution adds a bit to the one from murki:
<T> Observable.Transformer<T, T> parseHttpErrors() {
return new Observable.Transformer<T, T>() {
#Override
public Observable<T> call(Observable<T> observable) {
return observable.onErrorResumeNext(new Func1<Throwable, Observable<? extends T>>() {
#Override
public Observable<? extends T> call(Throwable throwable) {
if ( throwable instanceof HttpException ) {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(SERVER_URL) // write your url here
.addConverterFactory(GsonConverterFactory.create())
.build();
Converter<ResponseBody, Error> errorConverter =
retrofit.responseBodyConverter(Error.class, new Annotation[0]);
// Convert the error body into our Error type.
try {
Error error = errorConverter.convert(((HttpException) throwable).response().errorBody());
// Here you have two options, one is report this pojo back as error (onError() will be called),
return Observable.error(new Throwable(error.getMessage()));
}
catch (Exception e2) {
return Observable.error(new Throwable());
}
}
// if not the kind we're interested in, then just report the same error to onError()
return Observable.error(throwable);
}
});
}
};
}
and then at the onError(),
#Override
public void onError(Throwable e) {
progressBar.setVisibility(View.GONE); // optional
if ( !TextUtils.isEmpty(e.getMessage()) ) {
// show error as you like
return;
}
// show a default error if you wish
}