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;
}
Related
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.
I would like to implement a service(Web service) call in my application.
I blocked here for a while.
Previously I followed some of the below concepts.
like, AsyncTask class, Thread concepts and Handlers.
Recently I heard about the Retrofit.
Based on by experience, retrofit was good with high performance.
But it's not that much of reliable.
Example:
{
"Tag1":"Tag Value",
"TagArray":[ {"key1":"value","key2":"value"},{"key1":"value","key2":"value"},{"key1":"value","key2":"value"} ]
}
POJO:
public class Data{
String key1,key2;
sterres...
getters..
}
If the response have some other tags that are no need and the inside data only we need in our app i,e. "TagArray".
I need the handle only this response.
In such type of case this retrofit was failed.
Is there any other libraries or any other components to implement service calls in android with high performance are existed.
You can add your POJOs' or Beans' fields #Optional. This option comes from GSON that used in Retrofit as default.
EDIT :
public class ExamplePojo implements Serializable {
#SerializedName("TagArray")
public ArrayList<Keys> TagArray;
public ExamplePojo() {
TagArray = new ArrayList<>();
}
public static class Keys{
#SerializedName("key1")
public String key1;
#SerializedName("key2")
public String key2;
}
}
The attributes which you ignore shouldnt be added on POJO class or as mention above, add optional annotation if it can be null.
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.
I want to make POST request with custom header. I can't find information how to do this using AA Rest API - https://github.com/excilys/androidannotations/wiki/Rest%20API .
Should I use ClientHttpRequestInterceptor, which is used for authenticated requests?
https://github.com/excilys/androidannotations/wiki/Authenticated-Rest-Client
Thanks for any help!
There is currently an open issue for this : https://github.com/excilys/androidannotations/issues/323
For now, the only way to do this is with a custom ClientHttpRequestInterceptor. Here is a little example :
#EBean
public class CustomHeaderInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] data, ClientHttpRequestExecution execution) throws IOException {
request.getHeaders().add("myHeader", "value");
return execution.execute(request, data);
}
}
Then, you need to link it to the restTemplate, like this :
#EBean
public class MyService {
#RestService
RestClient restClient;
#Bean
MobileParametersInterceptor mobileParametersInterceptor;
#AfterInject
public void init() {
List<ClientHttpRequestInterceptor> interceptors = new ArrayList<ClientHttpRequestInterceptor>();
interceptors.add(mobileParametersInterceptor);
restClient.getRestTemplate().setInterceptors(interceptors);
}
}
Indeed you have to use the ClientHttpRequestInterceptor for custom header.
Currently, it's the only way I know.
See the official documentation of Spring-Android for more informations about the RestTemplate.