I'm trying make reuse of single retrofit api call by inherit from a base response class.
However I'm not able to do it.
I will try to make myself clear with example (It's not a concrete scenario. I'm just trying to figure out the main idea):
Having this response objects and api service:
public class UserDetailsResponse
{
private int userId;
}
public class ExtendedUserDetailsResponse extends UserDetailsResponse
{
private int userAdditionalId;
}
interface APIService
{
#GET("/UserDetails/")
Call<UserDetailsResponse> getUserDetails(#Query("id") String userId);
}
Is there a way of using getUserDetails api with ExtendedUserDetailsResponse object?
This one gives me compilation error:
mService.getUserDetails("123").enqueue(new Callback<ExtendedUserDetailsResponse>()
{
#Override
public void onResponse(Call<ExtendedUserDetailsResponse> call, Response<ExtendedUserDetailsResponse> response)
{
}
#Override
public void onFailure(Call<ExtendedUserDetailsResponse> call, Throwable t)
{
}
});
How can I solve this? or at least something similar to this, without using a new api call for the specific derived class?
Thanks!
You get an error because ExtendedUserDetailsResponse is a UserDetailsResponse, however, UserDetailsResponse is not necessarily an ExtendedUserDetailsResponse.
In order to make it generic, sign your method this way
Call< ExtendedUserDetailsResponse > getUserDetails(#Query("id") String userId);
Then ExtendedUserDetailsResponse will have access to userId
Remember to expose userId with getters and setters so that it get parsed.
You are getting compilation error because you are using the wrong callback object:
Just change this line:
Call<UserDetailsResponse> getUserDetails(#Query("id") String userId);
to
Call<ExtendedUserDetailsResponse> getUserDetails(#Query("id") String userId);
Or depending on the response change the object in the callback
Note that ExtendedUserDetailsResponse will have userId so you can use ExtendedUserDetailsResponse even if the server returns object of type UserDetailsResponse or ExtendedUserDetailsResponse.
Related
Before using rx.Observable, I used a custom callback with retrofit so I can add some specific logic for handling response/error and not have to do that inside the callback for every request as boilerplate code.
I force users to use the custom callback by putting it in the method signature like this:
#GET("/user_endpoint/")
void getUser(CustomCallback<User> callback);
#GET("/profile_endpoint/")
void getProfile(CustomCallback<Profile> callback);
but now that I'm returning an Observable:
#GET("/user_endpoint/")
Observable<User> getUser();
#GET("/profile_endpoint/")
Observable<Profile> getProfile();
I can't figure out a way to make sure that a custom callback always proxies the error/response.
Also, with retrofit2.0, how can I force the user to use a custom callback with the returned Call object?
CustomCallback for reference:
public abstract class CustomCallback<T> implements Callback<T> {
#Override public final void success(T t, Response response) {
// do some logic
onSuccess(t);
}
#Override public final void failure(RetrofitError error) {
// do something with the error here such as show a Toast
Toast.makeText(Application.getInstance(), error.getLocalizedMessage(), Toast.LENGTH_SHORT).show();
onFailure(error);
}
public abstract void onSuccess(T response);
public abstract void onFailure(Throwable error);
}
Stop. You're thinking this the wrong way.
Instead consider this: You have the normal Retrofit interface:
interface Foo {
#GET("/user_endpoint/")
Observable<User> getUser();
}
And then you have your decorator class:
public class FooDecorator implements Foo {
private Foo delegate = ...; // inject or create the Retrofit instance.
#Override
public Observable<User> getUser() {
return delegate.getUser().doOnNext(...).doOnError(...);
}
}
Then you use only the second class everywhere in your code (preferably just let the DI system use that) and you're set.
If you're feeling adventurous, you could even adapt the RxJavaCallAdapterFactory so that it modifies the returned observables without the need of a custom class.
I am new in Android and Retrofit and I am facing one problem.
I want to have my lets say "ServerCommunication" class (singelton) where all Retrofit magic is done and it will have public methods where REST calls are done.
I want to use this "ServerCommunication" instance in my activities to call Rest service, but thats it. Application logic should be done in activity. So this way some activity Login calls method Login(POJORequest) in "ServerCommunication) where actual REST call via Retrofit framework is done and some POJOResponse is returned. So Activity doesn't care about REST communication while ServerCommunication doesn't care about what logic that should be applied to response from REST service since.
With retrofit 2 I do not understand how I can block Activity to wait for response from retrofit and how it can be returned. Well, I might think I can use some callback methods in activity so those methods can be called from ServerCommunication" in OnPostExecute() to apply some logic based on data from response. It's just I think it should be simpler approach.
Well, to clarify all this mess above imagine simple case: You have data in you main activity, you pass this data to your communication class where REST call is done and response is received. This response must be validated in order to continue. And you want this validation to be done in main activity and NOT in communication class.
What is pattern to do that in Android with Retrofit2 ?
Thank you in advance
What I normally do:
Create your Interface (where you have all your REST methods - GET & POST etc)
Create a class that does the actual calls with corresponding methods (refer to the interface REST methods). I would call it something like ServiceAPIImplementor. This is where you actually create your Retrofit adapter.
In your activity, create an instance of your implementor class and call the methods and pass the expected arguments.
After calling the methods, you should probably show a progress dialog to let the user know that something is going on.
When the onResponse or onFailure method is called, use an Event pattern (EventBus library?) to notify the activity that the network operation has been completed. Once the activity has received the notification, it should then dismiss the progress dialog and update the UI accordingly - with the newly received data or completed operation (expected outcome).
I hope this helps you get closer to what you are trying to achieve!
Service interface (IPhotoService):
#GET("/photos/kudos")
Call<String> fetchKudos(#Header("Authorization") String authorization,
#Query("offset") int offset, #Query("mt") boolean mt);
Service impl (PhotoService):
private GoApiProvider<IPhotoService> mGoProvider = new GoApiProvider<>();
public Promiser<List<Photo>, HttpError> fetchKudos() {
return new Promiser<>((resolve, reject) ->
mGoProvider.getService(IPhotoService.class).fetchKudos(mSession.getToken(),
mOffsetKudos, true).enqueue(new Callback<String>() {
#Override
public void onResponse(Call<String> call, Response<String> response) {
if (response.isSuccessful()) {
PhotoParser JSON = new PhotoParser();
try {
mOffsetKudos = mOffsetKudos + 20;
resolve.run(JSON.photosFromJson(response.body()));
} catch (JSONException e) {
Log.e("fetchKudos", e.toString());
}
} else {
reject.run(new HttpError(response.code(), response.message()));
}
}
#Override
public void onFailure(Call<String> call, Throwable t) {
reject.run(new HttpError(YPErrorType.Undefined.getType(), t.getMessage()));
}
})
);
}
Activity or Fragment :
private void loadPhoto() {
new PhotoService().fetchKudos()
.success(this::resultSucceeded)
.error(this::resultError);
}
private void resultSucceeded(List<Photo> photos) {
mPhotoAdapter.setItems(photos);
}
private void resultError(HttpError httpError) {
httpErrorToast(httpError);
}
If you want to use Promizer: Click here
I've got a basic setup using Robospice with Retrofit in a shell Android application making REST calls, parsing JSON response into a POJO and that I can then use to render in an Activity. I now want to use TLS only for transport security (not SSL). I've read that Retrofit with OkHttp can be used to achieve this but I don't know where to make the update in my code.
I have a basic interface:
public interface RandomAPI {
#GET("/users")
List<User> getUsers(#Path("owner") String owner, #Path("repo") String repo);
#GET("/users/{userid}")
User getUser(#Path("userid") int userID);
}
I have a Service:
public class RandomService extends RetrofitGsonSpiceService {
private final static String BASE_URL = "http://jsonplaceholder.typicode.com";
#Override
public void onCreate() {
super.onCreate();
addRetrofitInterface(RandomAPI.class);
}
#Override
protected String getServerUrl() {
return BASE_URL;
}
}
and finally a request:
public class RandomRequest extends RetrofitSpiceRequest<User, RandomAPI> {
private int userID;
public RandomRequest(int userID) {
super(User.class, RandomAPI.class);
this.userID = userID;
}
#Override
public User loadDataFromNetwork() throws Exception {
return getService().getUser(userID);
}
}
I'm guessing I need to update the Service but not really sure how. I really like the simplicity of this pattern so would like to keep it if possible. I can drop the OkHttp jars into the application but I don't know how to get at the actual implementation of the service, or how to add my custom one so that all requests use it.
Has any one had experience with this that could share some code snippets or point me to an example?
~~ EDIT ~~
Looking into the API for Robospice, looks like my request can just extend SpiceRequest, then within the loadFromNetwork() method I just do plain Retrofit and OkHTTP stuff. Is that the only way though? Thought there would be a way to set your own RestAdapter implementation in RetrofitSpiceService instead of just using the default.
So to do this is actually quite simple. Create a class which extends RetrofitGsonSpiceService and override the createRestAdapterBuilder() method.
e.g.
#Override
protected Builder createRestAdapterBuilder() {
RestAdapter.Builder builder = new RestAdapter.Builder()
.setEndpoint(SERVICE_URL)
.setRequestInterceptor(requestInterceptor);
return builder;
}
I have a function that I want to test which runs in an okHttp callback. I'm trying to test it using Robolectrics but the callback is never executed. I presume that is because the test moves on after request without waiting for okHttp to return. So far I've tried:
ShadowLooper.pauseMainLooper();
Robolectric.flushBackgroundScheduler();
ShadowLooper.unPauseMainLooper();
but that didn't work. Any suggestions?
EDIT:
Here's an example of my code:
ApiClient.sendSomeDataToServer(data, callback);
Where ApiClient is a helper class containing okHttp client. sendSomeDataToServer API call looks something like this:
public static void sendSomeDataToServer(MyObject data, Callback callback){
final Request request = new Request.Builder()
.url(API_SOME_URL)
.post(RequestBody.create(JSON, myObject.getAsJson().toString()))
.build();
sHttpClient.newCall(request).enqueue(callback);
}
Where sHttpClient is an initialised OkHttpClient.
I can test the execution of above by forcing Thread.sleep(5000) inside my test code and providing custom callback. The code I'm trying to test is inside the callback. Any suggestions how I can test that? I really don't want to change the main code to fit the test framework - should be the other way round.
Lets assume you have next code. Interface:
#GET("/user/{id}/photo")
void listUsers(#Path("id") int id, Callback<Photo> cb);
Implementation:
public void fetchData() {
RestAdapter restAdapter = new RestAdapter.Builder()
.setServer("baseURL")
.build();
ClientInterface service = restAdapter.create(ClientInterface.class);
Callback<Photo> callback = new Callback<Photo>() {
#Override
public void success(Photo o, Response response) {
}
#Override
public void failure(RetrofitError retrofitError) {
}
};
service.listUsers(435, callback);
}
First of all you need to change service instantiation to service injection (as parameter or field). I will do it as parameter:
public void fetchData(ClientInterface clients) {
}
After this text is quite trivial:
#Test
public void checkThatServiceSuccessIsProcessed() {
ClientInterface mockedClients = mock(ClientInterface.class);
activity.fetchData(mockedClients);
// get callback
ArgumentCaptor<Callback<Photo>> captor = (ArgumentCaptor<Callback<Photo>>)ArgumentCaptor.forClass(Callback.class);
verify(mockedInterface).listUsers(anything(), captor.capture());
Callback<Photo> passedCallback = captor.value();
// run callback
callback.success(...);
// check your conditions
}
The used library for mocking and verifying is Mockito.
There will be one warning with captor instantiation because of generics but it fixable if you will use #Captor annotation instead of creating captor by hands.
The parameter injection is not perfect, especially for case of activities. This was used to simplify example. Consider for proper injection with library or without. I would encourage you to try Dagger for injections
You can use ArgumentCaptor (Mockito's class).
Reference:
http://www.mdswanson.com/blog/2013/12/16/reliable-android-http-testing-with-retrofit-and-mockito.html
I'm dealing with an API that I don't control which returns error responses in JSON format, but doesn't return a non-200 response code in that case. Is it still possible to get anything that is an error (as determined by the presence of an "error" property) in the failure callback when using Retrofit? It's safe to assume I can recognise error responses from that API by looking at the message contents.
This is an application-level distinction which means Retrofit doesn't (and shouldn't) care about it.
There's three ways to accomplish what you want, each of which maps to a behavior of Retrofit.
For asynchronous invocation you can use a custom Callback subtype which does the mapping.
public abstract class MyCallback<T extends MyResponse> implements Callback<T> {
#Override public final void success(T data, Response response) {
if (!data.success) {
success(data);
} else {
error(data.error, response);
}
// This is just an example of what a potential common handler could look like.
}
public abstract void success(T data);
public abstract void httpError(Error error, Response response);
}
If you are using the experimental RxJava support you should map the Observable through something which extracts the error.
service.doSomething()
.map(new Func1<SomethingResponse, SomethingResponse>() {
})
.observeOn(AndroidSchedulers.mainThread())
.subscribe(..);
Finally, for synchronous you can obviously check at every call site, but you can also wrap the API.
public final class WrappedService implements Service {
private final Service real;
#Override public SomethingResponse doSomething() {
return handleError(real.doSomething());
}
private static <T extends MyResponse> T handleError(T data) {
if (!data.success) {
throw new SomeException(data.error);
}
return data;
}
}
(Hint: You can also use a Proxy to do this for every method automatically!)
The important thing to remember here is that Retrofit's responsibility is to map your HTTP API to a Java API. It is not responsible for applying application-level behavior or constraints to the request or response data. That remains your responsibility.
Looks this is possible in the Converter, from the javadoc:
ConversionException - if conversion was unable to complete. This will trigger a call to
Callback.failure(retrofit.RetrofitError) or throw a RetrofitError. The exception message
should report all necessary information about its cause as the response body will be set to
null.