I have this method that I am trying to pull data from an API, and then update the text view. Everything works except getRecipeName doesn't finish after the "end Method" log. .getRecipeName() uses RetroFit to pull from an API.
I am currently learning MVP, Dagger, RxJava, and Butterknife all at once using
Mindork's Github page on MVP Architecture
I commented out the .subscribeOn and .observeOn to see the result difference and nothing changed.
#Override
public void onRandomButtonClicked() {
getMvpView().showLoading();
Log.e(TAG, "Random Method Open");
getCompositeDisposable().add(getDataManager()
.getRecipeName()
//.subscribeOn(getSchedulerProvider().io())
//.observeOn(getSchedulerProvider().ui())
.subscribe(new Consumer<String>() {
#Override
public void accept(String s) throws Exception {
Log.e(TAG, "accept");
getMvpView().updateTextView(title);
}
}));
Log.e(TAG, "end method");
}
Here is my getRecipeName() method
#Override
public Observable<String> getRecipeName() {
/*Create handle for the RetrofitInstance interface*/
GetDataService service = RetrofitClientInstance.getRetrofitInstance().create(GetDataService.class);
Call<RecipeList> call = service.getRecipe();
call.enqueue(new Callback<RecipeList>() {
#Override
public void onResponse(#NonNull Call<RecipeList> call, #NonNull retrofit2.Response<RecipeList> response) {
Log.e("onResponse","Recipe is Successful = " + response.isSuccessful());
//if response is false then skip to avoid null object reference
if (response.isSuccessful()) {
RecipeList drinkRecipe = response.body();
List<Recipe> recipes = drinkRecipe.getDrinks();
jokeText = String.valueOf(recipes.size());
Recipe myRecipe = recipes.get(0);
jokeText = myRecipe.getStrDrink();
Log.e("On Response", "Result2: " + jokeText);
}
//jokeText = "null";
}
#Override
public void onFailure(Call<RecipeList> call, Throwable t) {
Log.e("On Response","Failure");
}
});
//return jokeText;
return Observable.fromCallable(new Callable<String>() {
#Override
public String call() throws Exception {
return jokeText;
}
});
}
Solution
So as the comments stated RxJava Adapter was the correct way to go. I will just post my working code on myself using the adapter. I found it very difficult to find a working example.
//single api call using retrofit and rxjava
#SuppressLint("CheckResult")
private void getRandomButtonClick(){
retrofit = RetrofitClientInstance.getRetrofitInstance();
retrofit.create(GetDataService.class).getRecipe()
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(this::handleResults, this::handleError );
}
private void handleResults(RecipeList recipeList) {
int i = recipeList.getDrinks().size();
Log.e(TAG, "size is: "+ i);
Recipe recipe = recipeList.getDrinks().get(0);
getMvpView().updateTextView(recipe.getStrDrink());
}
private void handleError(Throwable t){
Log.e("Observer", "");
}
My Retrofit Client Instance
public static Retrofit getRetrofitInstance() {
if (retrofit == null) {
retrofit = new retrofit2.Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
return retrofit;
}
My Interface
public interface GetDataService {
//#Headers({})
#GET("random.php")
Observable<RecipeList> getRecipe();
I found a great resource to reference for me to correctly implement this. Retrofit Android
The reason is because your observable is returning jokeText every time it is subscribed upon. It returns immediately after invocation and will not wait for your network operation.
One possible solution is to use the RxJavaCallAdapter. Link here: https://github.com/square/retrofit/tree/master/retrofit-adapters/rxjava2
It will automatically convert your API returns to observables. No need to manually invoke retrofit requests. Just process the response and convert it to your desired object from there.
Another approach would be to wrap your entire sequence in an Observable.create or Observable.fromAsync.
Related
I am using .aar library in my app.
It has one Interface for the Network delegation which I need to overwrite.
ApiResponse executeApiCall(String url, HTTPMethod method, String params)
I am using Retrofit for the network calls. I need to convert the Synchronous call to USE asynchronous.
#Override
public ApiResponse executeApiCall(String url, HTTPMethod method, String params) {
ApiResponse apiResponse;
try {
// Synchronous Call
Call<String> call = RestClient.get().getStringResponse(url, method, params);
Response<String> response = call.execute();
apiResponse = new ApiResponse(response.code(), response.body());
} catch (Exception e) {
apiResponse = new ApiResponse();
}
return apiResponse;
}
Now I am stuck in how to use Asynchronous call within the Network Interface I must overwrite.
#Override
public ApiResponse executeApiCall(String url, HTTPMethod method, String params) {
ApiResponse apiResponse;
// Asynchronous Call
Call<String> call = RestClient.get().getStringResponse(url, method, params);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(#NotNull Call<String> call, #NotNull Response<String> response) {
if (response.isSuccessful()) {
apiResponse = new ApiResponse(response.code(), response.body());
}
}
#Override
public void onFailure(#NotNull Call<String> call, #NotNull Throwable t) {
apiResponse = new ApiResponse();
}
});
return apiResponse;
}
I can not change the Network delegation interface. I must overwrite it and I need to use retrofit Asynchronous.
Your feedback is most appreciated. Thanks guys.
I found some code on a different stack overflow thread. Here is how you could use the code to achieve what you are looking for.
Here is the class
public abstract class AsyncRunnable<T> {
protected abstract void run(AtomicReference<T> notifier);
protected final void finish(AtomicReference<T> notifier, T result) {
synchronized (notifier) {
notifier.set(result);
notifier.notify();
}
}
public static <T> T wait(AsyncRunnable<T> runnable) {
final AtomicReference<T> notifier = new AtomicReference<>();
// run the asynchronous code
runnable.run(notifier);
// wait for the asynchronous code to finish
synchronized (notifier) {
while (notifier.get() == null) {
try {
notifier.wait();
} catch (InterruptedException ignore) {}
}
}
// return the result of the asynchronous code
return notifier.get();
}
}
You can use it like this
ApiResponse result = AsyncRunnable.wait(new AsyncRunnable<ApiResponse>() {
#Override
public void run(final AtomicReference<String> notifier) {
// here goes your async code, e.g.:
new Thread(new Runnable() {
#Override
public void run() {
Call<String> call =
RestClient.get().getStringResponse(url, method, params);
call.enqueue(new Callback<String>() {
#Override
public void onResponse(#NotNull Call<String> call,
#NotNull
Response<String> response) {
if (response.isSuccessful()) {
apiResponse = new
ApiResponse(response.code(),
response.body());
finish(notifier, apiResponse);
}
}
#Override
public void onFailure(#NotNull Call<String> call, #NotNull Throwable t) {
apiResponse = new ApiResponse();
finish(notifier, apiResponse);
}
});
}
}).start();
}
});
We wait for the response to come from the callback and notify the AsyncRunnable class.
I feel you, I had a similar problem with a library that needed to do heavy work (non-UI) on the UI thread. Yep, sounds like a psycopath's lib. Coroutines runBlocking are your friend. I realize that you're asking for a rx-java solution, but can't beat coroutines for this.
/**
* You can edit, run, and share this code.
* play.kotlinlang.org
*/
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
fun main() {
val apiResponse = executeApiCall()
println(apiResponse) // prints "response"
println("goodbye, world!!!")
}
/**
* Synchronous function that waits for coroutine to finish
*/
fun executeApiCall():String {
return runBlocking(Dispatchers.IO){
retrofitCall()
}
}
/**
* Method that invokes Retrofit call in the background.
*/
suspend fun retrofitCall():String{
// RestClient.get() ...
return "response"
}
Note that if you call executeApiCall from UI thread you still will block the UI thread. So possible you need something like lifecycleScope.launch{executeApiCall() } if you're inside an activity or fragment (gradle dependency androidx.lifecycle:lifecycle-runtime-ktx:2.1.0), or CoroutineScope(Dispatchers.IO).launch { executeApiCall() } otherwise.
I have a Restful API whos return a Java Object for me. When return that object it is still empty, because the async thread is still working. How can get that response and return then to my Presenter and it directs the correct response to the view?
That is my retrofit call:
public String checkUser(final ModelUser modelUser) throws IOException {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UserRetrofitAPI.BASE_SERVICE)
.addConverterFactory(GsonConverterFactory.create())
.build();
UserRetrofitAPI userRetrofitAPI = retrofit.create(UserRetrofitAPI.class);
Call<ModelUser> requestCheckUser = userRetrofitAPI.checkUser(modelUser.getUser(), modelUser.getPassword());
requestCheckUser.enqueue(new Callback<ModelUser>() {
#Override
public void onResponse(Call<ModelUser> call, retrofit2.Response<ModelUser> response) {
if(!response.isSuccessful()){
myModelUser = new ModelUser(modelUser.getUser(),modelUser.getPassword(), String.valueOf(response.code()));
} else {
ModelUser modelUserChecked = response.body();
myModelUser = modelUserChecked;
}
}
#Override
public void onFailure(Call<ModelUser> call, Throwable t) {
Exception ex = new Exception(t);
myModelUser = new ModelUser(modelUser.getUser(), modelUser.getPassword(), ex.toString());
}
});
return myModelUser.getResponse();
}
when I do this debugging, it works, by processing time.
help me?
You shouldn't return that directly.
As you mentioned Retrofit response is updated in background thread.
I would suggest to return requestCheckUser only and observe that in your Presenter
public Call<ModelUser> checkUser(final ModelUser modelUser) throws IOException {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(UserRetrofitAPI.BASE_SERVICE)
.addConverterFactory(GsonConverterFactory.create())
.build();
UserRetrofitAPI userRetrofitAPI = retrofit.create(UserRetrofitAPI.class);
Call<ModelUser> requestCheckUser = userRetrofitAPI.checkUser(modelUser.getUser(), modelUser.getPassword());
return requestCheckUser;
}
Observe response of that call in Presenter and perform required operations as follows
checkUser(modelUser).enqueue(new Callback<ModelUser>() {
#Override
public void onResponse(Call<ModelUser> call, retrofit2.Response<ModelUser> response) {
if(!response.isSuccessful()){
myModelUser = new ModelUser(modelUser.getUser(),modelUser.getPassword(), String.valueOf(response.code()));
} else {
ModelUser modelUserChecked = response.body();
myModelUser = modelUserChecked;
}
}
#Override
public void onFailure(Call<ModelUser> call, Throwable t) {
Exception ex = new Exception(t);
myModelUser = new ModelUser(modelUser.getUser(), modelUser.getPassword(), ex.toString());
}
});
This would be the simple option and will satisfy this use case and scope.
You can use custom Interface Listeners if you don't prefer to write observer in Presenter.
I would recommend to look into RxJava and use it with Retrofit to convert this into more maintainable code
I am currently developing android app which uses Retrofit & OkHttpClient to get/send data from the server.
That was great when calling my own server, while it runs into 404 error when trying to call google map api.
The following represents response with error.
Response{protocol=h2, code=404, message=, url=https://maps.googleapis.com/maps%2Fapi%2Fgeocode%2Fjson%3Fkey=defesdvmdkeidm&latlng=11.586215,104.893197}
This is obviously because '/' and '?' was encoded into "%2F" and "%3F".
The solution could be prevent urlencode for those special characters, but couldn't make it.
What I tried is add custom header "Content-Type:application/x-www-form-urlencoded; charset=utf-8" to OkHttpClient via intercepter but that does not work.
Best detailed response will be appreciated.
Regards.
private Retrofit createRetrofit(OkHttpClient client, String _baseUrl) {
return new Retrofit.Builder()
.baseUrl(_baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(client)
.build();
}
private Retrofit createGoogleRetrofit() {
return createRetrofit(createGoogleClient(), baseUrl);
}
public DenningService getGoogleService() {
_baseUrl = "https://maps.googleapis.com/";
final Retrofit retrofit = createGoogleRetrofit();
return retrofit.create(DenningService.class);
}
public interface DenningService {
#GET("{url}")
#Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")
Single getEncodedRequest(#Path("url") String url);
}
private void sendRequest(final CompositeCompletion completion, final ErrorHandler errorHandler) {
mCompositeDisposable.add(mSingle.
subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.map(new Function() {
#Override
public JsonElement apply(JsonElement jsonElement) throws Exception {
return jsonElement;
}
})
.subscribeWith(new DisposableSingleObserver() {
#Override
public void onSuccess(JsonElement jsonElement) {
completion.parseResponse(jsonElement);
}
#Override
public void onError(Throwable e) {
if (e instanceof HttpException && ((HttpException) e).code() == 410) {
errorHandler.handleError("Session expired. Please log in again.");
} else {
errorHandler.handleError(e.getMessage());
}
e.printStackTrace();
}
})
);
}
public void sendGoogleGet(String url, final CompositeCompletion completion) {
mSingle = getGoogleService().getEncodedRequest(url);
sendRequest(completion, new ErrorHandler() {
#Override
public void handleError(String error) {
ErrorUtils.showError(context, error);
}
});
}
The problem is in the definition of your Retrofit service interface and the values you pass to it.
public interface DenningService {
#GET("{url}")
#Headers("Content-Type:application/x-www-form-urlencoded; charset=utf-8")
Single getEncodedRequest(#Path("url") String url);
}
From what you've posted, I'm going to assume the value of url is:
maps/api/geocode/json?key=defesdvmdkeidm&latlng=11.586215,104.893197
Here's how it should look:
public interface DenningService {
#FormUrlEncoded
#GET("/maps/api/geocode/json")
Single getEncodedRequest(#Field("key") String key,
#Field("latlng") String latlng);
}
And then you'd call it like this:
mSingle = getGoogleService().getEncodedRequest(key, latlng);
Of course, you will have to figure out how to separate the key and latlng parameters out of the current url string.
Edit
It's not obvious to me whether or not you actually want your request to be application/x-www-form-urlencoded, or if you were just trying that to see if it solved your problem. If you do not want it, then your interface would look like this instead:
public interface DenningService {
#GET("/maps/api/geocode/json")
Single getEncodedRequest(#Query("key") String key,
#Query("latlng") String latlng);
}
I am new to RxJava. I want to fetch data from the JSON API. Assume there are two APIs, API 1 and API 2. We fetch a JSON object "mediaId" from API 1. Now, I want to fetch JSON from API 2 with "mediaId". How can I achieve this using RxJava, along with retrofit in Android?
public void gettdata(final Listerner listerner){
postitemses= new ArrayList<>();
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://www.mytrendin.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
APiService networkAPI = retrofit.create(APiService.class);
Observable<List<Postitems>> observable = networkAPI.getFriendObservable()
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread());
observable.subscribe(new Observer<List<Postitems>>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
listerner.onFailure("oops... Something went wrong");
}
#Override
public void onNext(List<Postitems> postitemsList1) {
Postitems postitems;
for (int i=0;i<postitemsList1.size();i++){
postitems = new Postitems();
int id = postitemsList1.get(i).getId();
String title = postitemsList1.get(i).getTitle().getRendered();
String shortdesc= postitemsList1.get(i).getExcerpt().getRendered();
String mediaid= postitemsList1.get(i).getFeatured_media();
String authorid= postitemsList1.get(i).getAuthor();
String date = postitemsList1.get(i).getDate();
String slug = postitemsList1.get(i).getSlug();
Log.i("Hello-slug",""+slug);
String[] mediaurl= mydata(mediaid);
Log.i("Hello-mediaurl",""+mediaurl);
postitems.setId(id);
postitems.setDate(date);
postitems.setSlug(""+slug);
postitems.setPostExcerpt(shortdesc);
postitems.setPostTitle(title);
postitemses.add(postitems);
}
listerner.showpostitems(postitemses);
}
});
}
public String[] mydata(String mediaid){
final String[] mediaurl = new String[1];
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("https://www.mytrendin.com")
.build();
APiService aPiService = retrofit.create(APiService.class);
Call<Postitems> call = aPiService.getmediaurl(mediaid);
call.enqueue(new Callback<Postitems>() {
#Override
public void onResponse(Call<Postitems> call, Response<Postitems> response) {
Postitems postitemsList1 = response.body();
mediaurl[0] =postitemsList1.getGuid().getRendered();
// mediaurl[0][0] =postitemsList1.get(0).getGuid().getRendered();
}
#Override
public void onFailure(Call<Postitems> call, Throwable t) {
}
});
return mediaurl;
}
error occured
https://www.mytrendin.com
05-09 03:42:09.227 15315-15315/? D/AndroidRuntime: Shutting down VM
--------- beginning of crash
05-09 03:42:09.228 15315-15315/? E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mytrendin.mytrendin, PID: 15315
java.lang.NullPointerException: Attempt to invoke virtual method .mytrendin.dashboard.utils.Po stitems$Guid (ZygoteInit.java:755)
Sure you can use the merge operator along with the IO scheduler.By definition,merge can combine multiple Observables into one by merging their emissions.here is an example,
Observable<Integer> odds = Observable.just(1, 3, 5).subscribeOn(someScheduler);
Observable<Integer> evens = Observable.just(2, 4, 6);
Observable.merge(odds, evens)
.subscribe(new Subscriber<Integer>() {
#Override
public void onNext(Integer item) {
System.out.println("Next: " + item);
}
#Override
public void onError(Throwable error) {
System.err.println("Error: " + error.getMessage());
}
#Override
public void onCompleted() {
System.out.println("Sequence complete.");
}
});
Output :
Next: 1
Next: 3
Next: 5
Next: 2
Next: 4
Next: 6
Sequence complete.
Something like this in your case,
public Observable<Data> getMergedData() {
return Observable.merge(
networkRepository.getData().subscribeOn(Schedulers.io()),
networkRepository.getData().subscribeOn(Schedulers.io())
);
}
Alright there is another way to solve this, first create a observable for both API, then subscribe and observe changes from your first API subscription.Next create a PublishSubject instance. Which is useful because,once an Observer has subscribed, emits all subsequently observed items to the subscriber.For example publish string values from the api response.
private PublishSubject<String> subject = PublishSubject.create();
subject.observeOn(AndroidSchedulers.mainThread()).subscribeOn(Schedulers.io()).filter((s) -> s.size() > 0).subscribe(new Observer<String>() {
#Override
public void onSubscribe(Disposable d) {
}
#Override
public void onNext(String str) {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onComplete() {
}
});
Then to trigger the observable call onNext from the subject.
subject.onNext("some data from api");
Advantages, very flexible to changes to anywhere in your class scope.
Hope this helps.
for the below snippet
call.enqueue(new Callback<Postitems>() {
#Override
public void onResponse(Call<Postitems> call, Response<Postitems> response) {
Postitems postitemsList1 = response.body();
mediaurl[0] =postitemsList1.getGuid().getRendered();
// mediaurl[0][0] =postitemsList1.get(0).getGuid().getRendered();
//use the concept of publish subject here, which i detailed in answer, example
subject.onNext(postitemsList1.getGuid().getRendered());
//the string data will be passed to the above observable for the subject instance.
}
#Override
public void onFailure(Call<Postitems> call, Throwable t) {
}
});
I am new to RxJava so please forgive me if this sounds too newbie :-).
As of now I have an abstract CallbackClass that implements the Retofit Callback. There I catch the Callback's "onResponse" and "onError" methods and handle various error types before finally forwarding to the custom implemented methods.
I also use this centralized class to for request/response app logging and other stuff.
For example: for specific error codes from my sever I receive a new Auth token in the response body, refresh the token and then clone.enqueue the call.
There are of course several other global behaviors to the responses from my server.
Current solution (Without Rx):
public abstract void onResponse(Call<T> call, Response<T> response, boolean isSuccess);
public abstract void onFailure(Call<T> call, Response<T> response, Throwable t, boolean isTimeout);
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (_isCanceled) return;
if (response != null && !response.isSuccessful()) {
if (response.code() == "SomeCode" && retryCount < RETRY_LIMIT) {
TokenResponseModel newToken = null;
try {
newToken = new Gson().fromJson(new String(response.errorBody().bytes(), "UTF-8"), TokenResponseModel.class);
} catch (Exception e) {
e.printStackTrace();
}
SomeClass.token = newToken.token;
retryCount++;
call.clone().enqueue(this);
return;
}
}
} else {
onResponse(call, response, true);
removeFinishedRequest();
return;
}
onFailure(call, response, null, false);
removeFinishedRequest();
}
#Override
public void onFailure(Call<T> call, Throwable t) {
if (_isCanceled) return;
if (t instanceof UnknownHostException)
if (eventBus != null)
eventBus.post(new NoConnectionErrorEvent());
onFailure(call, null, t, false);
removeFinishedRequest();
}
My question is: Is there any way to have this sort of centralized response handling behavior before finally chaining (or retrying) back to the subscriber methods?
I found these 2 links which both have a nice starting point but not a concrete solution. Any help will be really appreciated.
Forcing request retry after custom API exceptions in RxJava
Retrofit 2 and RxJava error handling operators
Two links you provided are a really good starting point, which I used to construct solution to react to accidental
network errors happen sometimes due to temporary lack of network connection, or switch to low throughtput network standard, like EDGE, which causes SocketTimeoutException
server errors -> happen sometimes due to server overload
I have overriden CallAdapter.Factory to handle errors and react appropriately to them.
Import RetryWithDelayIf from the solution you found
Override CallAdapter.Factory to handle errors:
public class RxCallAdapterFactoryWithErrorHandling extends CallAdapter.Factory {
private final RxJavaCallAdapterFactory original;
public RxCallAdapterFactoryWithErrorHandling() {
original = RxJavaCallAdapterFactory.create();
}
#Override
public CallAdapter<?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit));
}
public class RxCallAdapterWrapper implements CallAdapter<Observable<?>> {
private final Retrofit retrofit;
private final CallAdapter<?> wrapped;
public RxCallAdapterWrapper(Retrofit retrofit, CallAdapter<?> wrapped) {
this.retrofit = retrofit;
this.wrapped = wrapped;
}
#Override
public Type responseType() {
return wrapped.responseType();
}
#SuppressWarnings("unchecked")
#Override
public <R> Observable<?> adapt(final Call<R> call) {
return ((Observable) wrapped.adapt(call)).onErrorResumeNext(new Func1<Throwable, Observable>() {
#Override
public Observable call(Throwable throwable) {
Throwable returnThrowable = throwable;
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException) throwable;
returnThrowable = httpException;
int responseCode = httpException.response().code();
if (NetworkUtils.isClientError(responseCode)) {
returnThrowable = new HttpClientException(throwable);
}
if (NetworkUtils.isServerError(responseCode)) {
returnThrowable = new HttpServerException(throwable);
}
}
if (throwable instanceof UnknownHostException) {
returnThrowable = throwable;
}
return Observable.error(returnThrowable);
}
}).retryWhen(new RetryWithDelayIf(3, DateUtils.SECOND_IN_MILLIS, new Func1<Throwable, Boolean>() {
#Override
public Boolean call(Throwable throwable) {
return throwable instanceof HttpServerException
|| throwable instanceof SocketTimeoutException
|| throwable instanceof UnknownHostException;
}
}));
}
}
}
HttpServerException is just a custom exception.
Use it in Retrofit.Builder
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(new RxCallAdapterFactoryWithErrorHandling())
.build();
Extra: If you wish to parse errors that come from API (error that don't invoke UnknownHostException, HttpException or MalformedJsonException or etc.) you need to override Factory and use custom one during building Retrofit instance. Parse the response and check if it contains errors. If yes, then throw error and error will be handled inside the method above.
have you consider using the rxjava adapter for retrofit?
https://mvnrepository.com/artifact/com.squareup.retrofit2/adapter-rxjava/2.1.0
in your gradle file add
compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
here's a interface for retrofit
public interface Service {
#GET("userauth/login?")
Observable<LoginResponse> getLogin(
#Query("v") String version,
#Query("username") String username,
#Query("password") String password);
}
and here's my implementation
Service.getLogin(
VERSION,
"username",
"password")
.subscribe(new Subscriber<LoginResponse>() {
#Override
public void onCompleted() {
}
#Override
public void onError(Throwable e) {
}
#Override
public void onNext(LoginResponse loginResponse) {
}
});
please note I'm using the gson converter factory to parse my response so I get an pojo (Plain Ole Java Object) returned.
See how you can do it.
Here is api call and pass Request model and response model in this.
public interface RestService {
//SEARCH_USER
#POST(SEARCH_USER_API_LINK)
Observable<SearchUserResponse> getSearchUser(#Body SearchUserRequest getSearchUserRequest);
}
This is the retrofit call,I used retrofit2
public RestService getRestService() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(ApiConstants.BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.client(getOkHttpClient())
.build();
return retrofit.create(RestService.class);
}
//get OkHttp instance
#Singleton
#Provides
public OkHttpClient getOkHttpClient() {
HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.interceptors().add(httpLoggingInterceptor);
builder.readTimeout(60, TimeUnit.SECONDS);
builder.connectTimeout(60, TimeUnit.SECONDS);
return builder.build();
}
This is the api call, call it in your activity.
#Inject
Scheduler mMainThread;
#Inject
Scheduler mNewThread;
//getSearchUser api method
public void getSearchUser(String user_id, String username) {
SearchUserRequest searchUserRequest = new SearchUserRequest(user_id, username);
mObjectRestService.getSearchUser(searchUserRequest).
subscribeOn(mNewThread).
observeOn(mMainThread).
subscribe(searchUserResponse -> {
Timber.e("searchUserResponse :" + searchUserResponse.getResponse().getResult());
if (isViewAttached()) {
getMvpView().hideProgress();
if (searchUserResponse.getResponse().getResult() == ApiConstants.STATUS_SUCCESS) {
} else {
}
}
}, throwable -> {
if (isViewAttached()) {
}
});
}
Hope this will help you.