i have a Retrofit service which is calling the following API http://api2.bigoven.com/recipe/1962911?api_key=my_api_key
this is my Retrofit Singleton Interface
public interface RecipeAPI {
String BASE_URL = "http://api2.bigoven.com/";
#GET("recipe/{id}")
Call<Recipe> getRecipe(#Path("id") int ID,
#Query("api_key") String apiKey);
class RecipesFetcher{
private static RecipeAPI service;
public static RecipeAPI getInstance(){
if (service==null) {
Retrofit retrofit = new Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.build();
service = retrofit.create(RecipeAPI.class);
return service;
} else {
return service;
}
}
}
}
this is the Full logcat:
http://codepad.org/lP1AhHCK
this is where i use the Retrofit interface but it always executes onFailure :
RecipeAPI.RecipesFetcher.getInstance().getRecipe(recipeID,API_KEY).enqueue(new Callback<Recipe>() {
#Override
public void onResponse(Call<Recipe> call, Response<Recipe> response) {
if (response.isSuccessful()){
//some code
}
else{
Toast.makeText(RecipeActivity.this, "Something went wrong!", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<Recipe> call, Throwable t) {
Toast.makeText(RecipeActivity.this, "Check your Internet Connection!", Toast.LENGTH_SHORT).show();
}
});
}
i tried to get call.toString()
it give me this messageretrofit2.executorcalladapterfactory$executorcallbackcall#428dc288
what is wrong with my code ?
Are you able to provide the endpoint documentation (the contract).
Should you be providing the API key in the header?
If you shouldn't, have you checked that the API key isnt null?
--edit--
Could you change the protocol to https?
The below docs mention that you are required to use https instead of http.
http://api2.bigoven.com/web/documentation/authentication-process
Related
I want to design API calls in such a way that it will be easy to handle success and failure responses easily from one place (instead of writing same code of call function for all APIs)
Here are the scenarios which I want to consider.
Handle success / failure and error responses like 4xx, 5xx etc of all APIs at one central place.
Want to cancel enqueue requests and also stop processing response if request is already sent in case of logout (because response parsing will modify some global data of app)
If access token has expired and 401 response received from cloud, it should get new token and then call API again automatically with new token.
My current implementation is not satisfying above requirements.
Is there any way to implement API calls which satisfy above requirements using Retrofit ?
Please suggest me a good design for this.
Here is my current implementation :
ApiInterface.java - It is an interface which contains different API calls definitions.
ApiClient.java - To get retrofit client object to call APIs.
ApiManager.java - It has methods to call APIs and parse their responses.
ApiInterface.java
public interface ApiInterface {
// Get Devices
#GET("https://example-base-url.com" + "/devices")
Call<ResponseBody> getDevices(#Header("Authorization) String token);
// Other APIs......
}
ApiClient.java
public class ApiClient {
private static Retrofit retrofitClient = null;
static Retrofit getClient(Context context) {
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.sslSocketFactory(sslContext.getSocketFactory(), systemDefaultTrustManager())
.connectTimeout(15, TimeUnit.SECONDS)
.writeTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.build();
retrofitClient = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.build();
}
}
ApiManager.java
public class ApiManager {
private static ApiManager apiManager;
public static ApiManager getInstance(Context context) {
if (apiManager == null) {
apiManager = new ApiManager(context);
}
return apiManager;
}
private ApiManager(Context context) {
this.context = context;
apiInterface = ApiClient.getClient(context).create(ApiInterface.class);
}
public void getDevices(ResponseListener listener) {
// API call and response handling
}
// Other API implementation
}
Update :
For 1st point, interceptor will be helpful to handle 4xx, 5xx responses globally according to this.
But interceptor will be in the ApiClient file and to inform UI or API caller component, need to pass success or failure result in callback I mean response listener.
How can I do that ? Any idea ?
For 3rd point, I know little bit about Retrofit Authenticator. I think for this point it is suitable but it requires synchronous call to get new token using refresh token.
How can I make asynchronous call to synchronous ? (Note : this call is not retrofit call)
By handling the success/failure responses at a central place I'll assume you want to get rid of repeated boilerplate based on the error parsing logic and how it may create UI side-effects for your app.
I'd probably suggest keeping things really simple by creating a custom abstraction for Callback which invokes your APIs for success/failure according to your domain logic.
Here's something fairly simple implementation for use-case (1) :
abstract class CustomCallback<T> implements Callback<T> {
abstract void onSuccess(T response);
abstract void onFailure(Throwable throwable);
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.isSuccessful()) {
onSuccess(response.body());
} else {
onFailure(new HttpException(response));
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
onFailure(t);
}
}
For use-case (2), to be able to cancel all enqueued calls upon a global event like logout you'd have to keep a reference to all such objects. Fortunately, Retrofit supports plugging in a custom call factory okhttp3.Call.Factory
You could use your implementation as a singleton to hold a collection of calls and in the event of a logout notify it to cancel all requests in-flight. Word of caution, do use weak references of such calls in the collection to avoid leaks/references to dead calls. (also you might want to brainstorm on the right collection to use or a periodic cleanup of weak references based on the transactions)
For use-case (3), Authenticator should work out fine since you've already figured out the usage there are 2 options -
Migrate the refresh token call to OkHttp/Retrofit and fire it synchronously
Use a count-down latch to make the authenticator wait for the async call to finish (with a timeout set to connection/read/write timeout for the refresh token API call)
Here's a sample implementation:
abstract class NetworkAuthenticator implements Authenticator {
private final SessionRepository sessionRepository;
public NetworkAuthenticator(SessionRepository repository) {
this.sessionRepository = repository;
}
public Request authenticate(#Nullable Route route, #NonNull Response response) {
String latestToken = getLatestToken(response);
// Refresh token failed, trigger a logout for the user
if (latestToken == null) {
logout();
return null;
}
return response
.request()
.newBuilder()
.header("AUTHORIZATION", latestToken)
.build();
}
private synchronized String getLatestToken(Response response) {
String currentToken = sessionRepository.getAccessToken();
// For a signed out user latest token would be empty
if (currentToken.isEmpty()) return null;
// If other calls received a 401 and landed here, pass them through with updated token
if (!getAuthToken(response.request()).equals(currentToken)) {
return currentToken;
} else {
return refreshToken();
}
}
private String getAuthToken(Request request) {
return request.header("AUTHORIZATION");
}
#Nullable
private String refreshToken() {
String result = null;
CountDownLatch countDownLatch = new CountDownLatch(1);
// Make async call to fetch token and update result in the callback
// Wait up to 10 seconds for the refresh token to succeed
try {
countDownLatch.await(10, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
return result;
}
abstract void logout();
}
I hope this helps with your network layer implementation
So, With the help of official sample in the retrofit github repository here: https://github.com/square/retrofit/blob/fbf1225e28e2094bec35f587b8933748b705d167/samples/src/main/java/com/example/retrofit/ErrorHandlingAdapter.java
The ErrorHandlingAdapter is the closest you can get to your requirement because it lets you control enqueuing of the call, creating the error callbacks, calling error callbacks on your own. Whether you want the caller to do some action or you want to handle it yourself in one place or just both.
So this is how you can create it. Do read the inline comments to understand.
public final class ErrorHandlingAdapter {
/**
* Here you'll decide how many methods you want the caller to have.
*/
interface MyCallback<T> {
void success(Response<T> response);
void error(String s);
}
/**
* This is your call type
*/
interface MyCall<T> {
void cancel();
void enqueue(MyCallback<T> callback);
#NotNull
MyCall<T> clone();
}
public static class ErrorHandlingCallAdapterFactory extends CallAdapter.Factory {
#Override
public #Nullable
CallAdapter<?, ?> get(
#NotNull Type returnType, #NotNull Annotation[] annotations, #NotNull Retrofit retrofit) {
if (getRawType(returnType) != MyCall.class) {
return null;
}
if (!(returnType instanceof ParameterizedType)) {
throw new IllegalStateException(
"MyCall must have generic type (e.g., MyCall<ResponseBody>)");
}
Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);
Executor callbackExecutor = retrofit.callbackExecutor();
return new ErrorHandlingCallAdapter<>(responseType, callbackExecutor);
}
private static final class ErrorHandlingCallAdapter<R> implements CallAdapter<R, MyCall<R>> {
private final Type responseType;
private final Executor callbackExecutor;
ErrorHandlingCallAdapter(Type responseType, Executor callbackExecutor) {
this.responseType = responseType;
this.callbackExecutor = callbackExecutor;
}
#NotNull
#Override
public Type responseType() {
return responseType;
}
#Override
public MyCall<R> adapt(#NotNull Call<R> call) {
return new MyCallAdapter<>(call, callbackExecutor);
}
}
}
static class MyCallAdapter<T> implements MyCall<T> {
private final Call<T> call;
private final Executor callbackExecutor;
MyCallAdapter(Call<T> call, Executor callbackExecutor) {
this.call = call;
this.callbackExecutor = callbackExecutor;
}
#Override
public void cancel() {
call.cancel();
}
#Override
public void enqueue(final MyCallback<T> callback) {
if (!SomeCondition.myCondition) {
// Don't enqueue the call if my condition doesn't satisfy
// it could be a flag in preferences like user isn't logged in or
// some static flag where you don't want to allow calls
return;
}
call.clone().enqueue(
new Callback<T>() {
#Override
public void onResponse(#NotNull Call<T> call, #NotNull Response<T> response) {
callbackExecutor.execute(() -> {
int code = response.code();
if (code >= 200 && code < 300) {
//success response
callback.success(response);
} else if (code == 401) {
// Unauthenticated so fetch the token again
getTheTokenAgain(callback);
} else if (code >= 400 && code < 500) {
//handle error the way you want
callback.error("Client error");
} else if (code >= 500 && code < 600) {
//handle error the way you want
callback.error("Server error");
} else {
//handle error the way you want
callback.error("Something went wrong");
}
});
}
#Override
public void onFailure(#NotNull Call<T> call, #NotNull Throwable t) {
callbackExecutor.execute(() -> {
if (t instanceof IOException) {
callback.error("IOException");
} else {
callback.error("Some exception");
}
});
}
});
}
private void getTheTokenAgain(MyCallback<T> callback) {
// Make the call to get the token & when token arrives enqueue it again
// Don't forget to put termination condition like 3 times, if still not successful
// then just log user out or show error
// This is just dummy callback, you'll need to make a
// call to fetch token
new MyTokenCallback() {
#Override
public void onTokenArrived(String token) {
//enqueue(callback); here
}
#Override
public void onTokenFetchFailed() {
callbackExecutor.execute(() -> {
callback.error("Counld't fetch token");
});
}
};
// This is for demo you should put it in success callback
SomeCondition.callCount++;
Log.d("MG-getTheTokenAgain", "Method called");
if (SomeCondition.callCount < 3) {
enqueue(callback);
} else {
callbackExecutor.execute(() -> {
callback.error("Counld't fetch token");
});
}
}
#NotNull
#Override
public MyCall<T> clone() {
return new MyCallAdapter<>(call.clone(), callbackExecutor);
}
}
}
This is how you'll plug this adapter:
private void makeApiCall() {
//This is just for demo to generate 401 error you won't need this
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(chain -> {
Request request = chain.request().newBuilder()
.addHeader("Accept","application/json")
.addHeader("Authorization", "cdsc").build();
return chain.proceed(request);
});
Retrofit retrofit =
new Retrofit.Builder()
.baseUrl("http://httpbin.org/")
.addCallAdapterFactory(new ErrorHandlingAdapter.ErrorHandlingCallAdapterFactory())
.addConverterFactory(GsonConverterFactory.create())
.client(httpClient.build())
.build();
HttpBinService service = retrofit.create(HttpBinService.class);
ErrorHandlingAdapter.MyCall<Ip> ip = service.getIp();
ip.enqueue(
new ErrorHandlingAdapter.MyCallback<Ip>() {
#Override
public void success(Response<Ip> response) {
Log.d("MG-success", response.toString());
}
#Override
public void error(String s) {
Log.d("MG-error", s);
}
});
}
You might have to bend some things to your needs, but I think this could be good reference because it's in the official sample.
1. Handle success / failure and error responses like 4xx, 5xx etc of
all APIs at one central place.
Create following two classes:
ApiResponse.kt
class ApiResponse<T : Any> {
var status: Boolean = true
var message: String = ""
var data: T? = null
}
ApiCallback.kt
abstract class ApiCallback<T : Any> : Callback<ApiResponse<T>> {
abstract fun onSuccess(response: ApiResponse<T>)
abstract fun onFailure(response: ApiResponse<T>)
override fun onResponse(call: Call<ApiResponse<T>>, response: Response<ApiResponse<T>>) {
if (response.isSuccessful && response.body() != null && response.code() == 200) {
onSuccess(response.body()!!)
} else { // handle 4xx & 5xx error codes here
val resp = ApiResponse<T>()
resp.status = false
resp.message = response.message()
onFailure(resp)
}
}
override fun onFailure(call: Call<ApiResponse<T>>, t: Throwable) {
val response = ApiResponse<T>()
response.status = false
response.message = t.message.toString()
onFailure(response)
}
}
Now use the above ApiCallback class instead of Retrofit's Callback class to enqueue
2. Want to cancel enqueue requests and also stop processing response if request is already sent in case of logout (because response parsing will modify some global data of app)
You cannot stop processing a response midway, but what you can do is not update the ui or activity if the activity in question is not in foreground, which can be done with the help of LiveData from the MVVM Architecture.
3. If access token has expired and 401 response received from cloud, it should get new token and then call API again automatically with new token.
Create a TokenAuthenticator.java class like this
public class TokenAuthenticator implements Authenticator {
#Override
public Request authenticate(Proxy proxy, Response response) throws IOException {
// Refresh your access_token using a synchronous api request
newAccessToken = service.refreshToken();
// Add new header to rejected request and retry it
return response.request().newBuilder()
.header(AUTHORIZATION, newAccessToken)
.build();
}
#Override
public Request authenticateProxy(Proxy proxy, Response response) throws IOException {
// Null indicates no attempt to authenticate.
return null;
}
}
Attach an instance of the above authenticator to OkHttpClient like this
OkHttpClient okHttpClient = new OkHttpClient();
okHttpClient.setAuthenticator(authAuthenticator);
Then finally, attach okHttpClient to the Retrofit instance as you have already done
More info regarding the authenticator part can be found in this answer here
I need to log the request URL that Retrofit creates. I don't find any getter methods on Retrofit object or web interface that is generated via Retrofit. The following is my code, where I want to log the address of every request:
public void onRequestFoods() {
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Const.BASE_LOCAL)
.addConverterFactory(GsonConverterFactory.create())
.build();
FoodOrderInterface foodInterface = retrofit.create(FoodOrderInterface.class);
Log.d(TAG, "onRequestFoods: request url: ");
foodInterface.listFoods().enqueue(new Callback<FoodResponse>() {
#Override
public void onResponse(Call<FoodResponse> call, Response<FoodResponse> response) {
List<Food> foods = response.body().getBody().getFoods();
mPresenter.onResponse((ArrayList<Food>) foods);
}
#Override
public void onFailure(Call<FoodResponse> call, Throwable t) {
mPresenter.onRequestFailed(t.getMessage());
}
});
}
I think what you need is http logging interceptor the github repo has a straightforward example of how to get it up and running
I'm using retrofit 2 to make api call to my server but it get stucked when trying to make api call. This is my code
public interface GOTApi {
#GET("characters.json")
Call<GOTCharacterResponse> getCharacters();
}
Intermediate class to get the data
public class GOTCharacterResponse {
List<GOTCharacter> characters;
}
My class to make api call
public class GOTService {
public static final String BASE_URL = "https://project-8424324399725905479.firebaseio.com/";
public static GOTApi getGOTApi(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
return retrofit.create(GOTApi.class);
}
public static void getCharacters(){
getGOTApi().getCharacters().enqueue(new Callback<GOTCharacterResponse>() {
#Override
public void onResponse(Call<GOTCharacterResponse> call, Response<GOTCharacterResponse> response) {
if(response.isSuccessful()){
}
}
#Override
public void onFailure(Call<GOTCharacterResponse> call, Throwable t) {
int a = 0;
}
});
}
}
These are the libraries I'm using
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit:converter-gson:2.0.0-beta2'
compile 'com.squareup.okhttp3:okhttp:3.3.1'
It always get stucked in the getCharacters() method. Of course I have internet permission set in Mainfest.
You may try using Retrofit2 with RxJava, it is more convenient.
public Retrofit providedRetrofit(OkHttpClient okHttpClient){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJavaCallAdapterFactory.create())
.build();
return retrofit;
}
Your API interface will look like
public interface Api {
#GET("api/service/schedule/{filial}")
Observable<Response<GOTCharacter>> getSchedule(#Path("some_param") String param);
}
You also need to parse response from JSON. You didn't provided
GOTCharacter class, but you can create code from json response by using
http://www.jsonschema2pojo.org/ service
I think you are implementing wrong onResponse() OR Callback(), because I am using Retrofit 2 too, in which onResponse() looks like this:
#Override
public void onResponse(Response<ListJsonResponseRestaurant> response, Retrofit retrofit) {
...
...
}
I am using Retrofit 2.0 library in my android application by adding it into build.gradle file
// retrofit, gson
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.0.2'
compile 'com.squareup.retrofit2:converter-gson:2.0.2'
related code is given below
ApiInterface.java
public interface ApiInterface {
#GET("contacts/")
Call<ContactsModel> getContactsList();
}
ApiClient.java
public class ApiClient {
public static final String BASE_URL = "http://myexamplebaseurl/";
private static Retrofit retrofit = null;
public static Retrofit getClient() {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
MainActivity.java
ApiInterface apiService = ApiClient.getClient().create(ApiInterface.class);
Call<ContactsModel> call = apiService.getContactsList();
call.enqueue(new Callback<ContactsModel>() {
#Override
public void onResponse(Call<ContactsModel> call, Response<ContactsModel> response) {
if(response.isSuccessful()){
/*here is my data handling*/
}
}
#Override
public void onFailure(Call<ContactsModel> call, Throwable t) {
/*It is the request failure case,
I want to differentiate Request timeout, no internet connection and any other reason behind the request failure
*/
}
});
if we get status code as 4xx or 5xx even though onResponse() will called, so there we need handle that condition also.
Here my question is, How to differentiate reason for request failure i.e onFailure() by using Retrofit 2.0 in Android?
Here my question is, How to differentiate reason for request failure
by using Retrofit 2.0 in Android?
if you have a 4xx or 5xx error, onResponse is still called. There you have to check the response code of the code to check if everything was fine. E.g
if (response.code() < 400) {
in case of No Network connection, onFailure is called. There you could check the instance of the throwable. Typically an IOException
Recently I started using Retrofit 2 and I faced an issue with parsing empty response body. I have a server which responds only with http code without any content inside the response body.
How can I handle only meta information about server response (headers, status code etc)?
Edit:
As Jake Wharton points out,
#GET("/path/to/get")
Call<Void> getMyData(/* your args here */);
is the best way to go versus my original response --
You can just return a ResponseBody, which will bypass parsing the response.
#GET("/path/to/get")
Call<ResponseBody> getMyData(/* your args here */);
Then in your call,
Call<ResponseBody> dataCall = myApi.getMyData();
dataCall.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Response<ResponseBody> response) {
// use response.code, response.headers, etc.
}
#Override
public void onFailure(Throwable t) {
// handle failure
}
});
If you use RxJava, then it's better to use Completable in this case
Represents a deferred computation without any value but only indication for completion or exception. The class follows a similar event pattern as Reactive-Streams: onSubscribe (onError|onComplete)?
http://reactivex.io/RxJava/2.x/javadoc/io/reactivex/Completable.html
in the accepted answer:
#GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);
If the endpoint returns failure response code, it will still be in the onNext and you will have to check the response code yourself.
However, if you use Completable.
#GET("/path/to/get")
Completable getMyData(/* your args here */);
you will have only onComplete and onError.
if the response code is success it will fire the onComplete else it will fire onError.
If you are using rxjava, use something like :
#GET("/path/to/get")
Observable<Response<Void>> getMyData(/* your args here */);
With kotlin, using the return type Call<Void> still throws IllegalArgumentException: Unable to create converter for retrofit2.Call<java.lang.Void>
Using Response instead of Call resolved the issue
#DELETE("user/data")
suspend fun deleteUserData(): Response<Void>
Here is an example Kotlin in MVVM with service, Repository and ViewModel:
Service:
#POST("/logout")
suspend fun logout(#Header("Authorization") token: String):Response<Unit>
Repository:
//logout
private val mLogoutResponse = MutableLiveData<String>()
val logoutResponse: LiveData<String>
get() {
return mLogoutResponse
}
suspend fun logout(token: String) {
try {
val result=quizzerProfileApi.logout(token)
if(result.code()!=0)
{
mLogoutResponse.postValue(result.code().toString())
}
} catch (e: Exception) {
Log.d("ProfileRepository", "logout: Error: $e")
}
}
ViewModel:
fun logout(token: String) {
viewModelScope.launch {
repository.logout(token)
}
}
val logoutResponseCd: LiveData<String>
get() = repository.logoutResponse
in Activity:
private fun logout() {
myViewModel.logout(token)
myViewModel.logoutResponseCd.observe(this, Observer {
if(it!="0"){
Log.d(TAG, "logout: code= $it")
finish()
}
else
Toast.makeText(this, "Error logging out: $it", Toast.LENGTH_SHORT).show()
})
}
Here is how I used it with Rx2 and Retrofit2, with PUT REST request:
My request had a json body but just http response code with empty body.
The Api client:
public class ApiClient {
public static final String TAG = ApiClient.class.getSimpleName();
private DevicesEndpoint apiEndpointInterface;
public DevicesEndpoint getApiService() {
Gson gson = new GsonBuilder()
.setLenient()
.create();
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClientBuilder.addInterceptor(logging);
OkHttpClient okHttpClient = okHttpClientBuilder.build();
apiEndpointInterface = new Retrofit.Builder()
.baseUrl(ApiContract.DEVICES_REST_URL)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(DevicesEndpoint.class);
return apiEndpointInterface;
}
The interface:
public interface DevicesEndpoint {
#Headers("Content-Type: application/json")
#PUT(ApiContract.DEVICES_ENDPOINT)
Observable<ResponseBody> sendDeviceDetails(#Body Device device);
}
Then to use it:
private void sendDeviceId(Device device){
ApiClient client = new ApiClient();
DevicesEndpoint apiService = client.getApiService();
Observable<ResponseBody> call = apiService.sendDeviceDetails(device);
Log.i(TAG, "sendDeviceId: about to send device ID");
call.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<ResponseBody>() {
#Override
public void onSubscribe(Disposable disposable) {
}
#Override
public void onNext(ResponseBody body) {
Log.i(TAG, "onNext");
}
#Override
public void onError(Throwable t) {
Log.e(TAG, "onError: ", t);
}
#Override
public void onComplete() {
Log.i(TAG, "onCompleted: sent device ID done");
}
});
}
You can try this one
Retrofit retrofit = new Retrofit.Builder().baseUrl(baseUrl)
.addConverterFactory(new NullOnEmptyConverterFactory())
.client(okHttpClient).build();
class NullOnEmptyConverterFactory extends Converter.Factory {
#Override
public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
return (Converter<ResponseBody, Object>) body -> {
if (body.source().exhausted()) return null;
return delegate.convert(body);
};
}
}
Kotlin, Retrofit
#POST
suspend fun accountVerification(
#Body requestBody: RequestBody
): Response<Unit>
and success can be check using
if (response.isSuccessful) { }