Retrofit Singleton Instance causing a 403 Issue in API calls? - java

I have setup a retrofit2 singleton instance which is accessed by all API methods. However, upon logging out the user and re-signing in, all API calls fail with a 403. The likely cause of this is retrofit re-using the previous destroyed access token and not resetting the retrofit instance.
Singleton:
public class RetroGenerator {
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder;
public static Retrofit retrofit;
static synchronized private Retrofit.Builder getBuilder() {
if (builder == null) {
initRetrofit();
}
return builder;
}
public static void initRetrofit() {
builder =
new Retrofit.Builder()
.baseUrl(SessionManager.getInstance().baseUrl)
.addConverterFactory(GsonConverterFactory.create(initGson()));
}
private static Gson initGson() {
return new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ").create();
}
public static Retrofit getRetrofit() {
return getBuilder().client(httpClient.build()).build();
}
public static <S> S createService(Class<S> serviceClass) {
return getRetrofit().create(serviceClass);
}
public static <S> S createService(Class<S> serviceClass, final Auth auth) {
if (retrofit == null) {
if (auth != null) {
//Adding request payload logging
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(logging);
}
httpClient.retryOnConnectionFailure(true);
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("Accept", "application/json")
.header("Authorization",
auth.token_type + " " + auth.access_token)
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
}
OkHttpClient client = httpClient.build();
if (retrofit == null) {
retrofit = getBuilder().client(client).build();
}
}
return retrofit.create(serviceClass);
}
public static void clearRetrofit(){
retrofit = null;
}
}
An API class would use this by:
RetroGenerator.createService(APIServices.class).getUsers().enqueue(new Callback<ArrayList<User>> () {
#Override
public void onResponse(Call <ArrayList<User>> call, Response <ArrayList<User>> response) {
if (response.isSuccessful()) {
callBack.onSuccess(response.body());
}
}
#Override
public void onFailure(Call <ArrayList<User>> call, Throwable t) {
}
});
To amend this, my attempt was to set the retrofit instance as null upon signing out. Thus, the use of clearRetrofit() method. However, the issue still exists and setting it null didn't fix the issue. What is the reoolution for this?

I solved this problem. Here's the write-up:
Problem:
I needed global access to a retrofit instance without having to re-initialise an instance every time
The code in the question was confusing the public retrofit instance with authenticated endpoints thus leading to a 401: Unauthorised error.
Solution:
The solution I've attached below was written to deliver the following:
- Re-usability of a retrofit instance by using a Singleton design pattern
Ability to write an interface APIServices and include all endpoints in it. This allows extensibility.
Ability to switch between a retrofit built for public endpoints and a retrofit built for authenticated endpoints.
Here's my solution:
https://gist.github.com/dinukapj/b315e5f4438bc670d7509f7aa7aaffdd

Related

retrofit api get request not firing the calback methods

I'm encountering a problem with retrofit :
Im trying to make a get request to this API: https://test.spaceflightnewsapi.net/api/v2/articles
But somehow the callbacks methods from the retrofit are not called.
I get this in the logcat/ the only thing related to the retrofit (okkhttp):
I/okhttp.OkHttpClient: --> GET https://test.spaceflightnewsapi.net/api/v2/articles
I/okhttp.OkHttpClient: --> END GET
here is my api interface:
public interface SpaceNewsApi {
String BASE_URL = "https://test.spaceflightnewsapi.net/api/v2/";
#GET("articles")
Call<List<SpaceNews>> getSpaceNewsArticles();
}
here is the retrofit client (note that i will be using two apis with the retrofit this is why i'm using two methods to build it), also i've added a loginginterceptor, I've change the Level to different types but only the things from above are the messages i get each time in the logs related to retrofit
public class RetrofitClients {
private static RetrofitClients instance = null;
private SpaceNewsApi mySpaceNewsApi = null;
private LaunchLibraryApi myLaunchLibraryApi = null;
public static synchronized RetrofitClients getInstance() {
if (instance == null) {
instance = new RetrofitClients();
}
return instance;
}
public SpaceNewsApi getMySpaceNewsApi() {
if(mySpaceNewsApi == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(SpaceNewsApi.BASE_URL)
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
mySpaceNewsApi = retrofit.create(SpaceNewsApi.class);
}
return mySpaceNewsApi;
}
public LaunchLibraryApi getMyLaunchLibraryApi(){
if(myLaunchLibraryApi == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(LaunchLibraryApi.BASE_URL)
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
myLaunchLibraryApi = retrofit.create(LaunchLibraryApi.class);
}
return myLaunchLibraryApi;
}
public static OkHttpClient getHttpClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
//TODO : remove logging interceptors as it is to be used for development purpose
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(300, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS)
.addInterceptor(logging)
.build();
return client;
}
}
I'm calling all of these in my main activity onCreate method like this:
//testing Api Call
Call<List<SpaceNews>> call = RetrofitClients.getInstance().getMySpaceNewsApi().getSpaceNewsArticles();
call.enqueue(new Callback<List<SpaceNews>>() {
#Override
public void onResponse(Call<List<SpaceNews>> call, Response<List<SpaceNews>> response) {
Log.d("gotApiData","got it");
}
#Override
public void onFailure(Call<List<SpaceNews>> call, Throwable t) {
Log.d("gotError","not got it");
t.printStackTrace();
}
});
How can I get this working,
I would appreciate any advice!
The problem I believe was with the RetrofitClients class, after adding a private constructor and moving the majority of code from the get***Api methods seems to solve the problem.
here is the modified RetrofitClients:
public class RetrofitClients {
private static RetrofitClients instance = null;
private SpaceNewsApi mySpaceNewsApi = null;
private LaunchLibraryApi myLaunchLibraryApi = null;
private RetrofitClients(){
if(mySpaceNewsApi == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(SpaceNewsApi.BASE_URL)
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
mySpaceNewsApi = retrofit.create(SpaceNewsApi.class);
}
if(myLaunchLibraryApi == null) {
Retrofit retrofit = new Retrofit.Builder().baseUrl(LaunchLibraryApi.BASE_URL)
.client(getHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
myLaunchLibraryApi = retrofit.create(LaunchLibraryApi.class);
}
}
public static synchronized RetrofitClients getInstance() {
if (instance == null) {
instance = new RetrofitClients();
}
return instance;
}
public SpaceNewsApi getMySpaceNewsApi() {
return mySpaceNewsApi;
}
public LaunchLibraryApi getMyLaunchLibraryApi(){
return myLaunchLibraryApi;
}
public static OkHttpClient getHttpClient() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.HEADERS);
//TODO : remove logging interceptors as it is to be used for development purpose
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(300, TimeUnit.SECONDS)
.readTimeout(300, TimeUnit.SECONDS)
.addInterceptor(logging)
.build();
return client;
}
}

Refresh Access Token Retrofit2 + RXJava2

This approach always worked when updating a token. That is, with each request if I received an error 401, the operator retryWhen() triggered it updated the token.
Here is the code:
private Observable<TokenModel> refreshAccessToken() {
Map<String, String> requestBody = new HashMap<>();
requestBody.put(Constants.EMAIL_KEY, Constants.API_EMAIL);
requestBody.put(Constants.PASSWORD_KEY, Constants.API_PASSWORD);
return RetrofitHelper.getApiService().getAccessToken(requestBody)
.subscribeOn(Schedulers.io())
.doOnNext((AccessToken refreshedToken) -> {
PreferencesHelper.putAccessToken(mContext, refreshedToken);
});
}
public Function<Observable<Throwable>, ObservableSource<?>> isUnauthorized (){
return throwableObservable -> throwableObservable.flatMap((Function<Throwable, ObservableSource<?>>) (Throwable throwable) -> {
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException) throwable;
if (httpException.code() == 401) {
return refreshAccessToken();
}
}
return Observable.error(throwable);
});
}
I call isUnauthorized() at the retryWhen() operator where I make a request to the server
class RetrofitHelper {
static ApiService getApiService() {
return initApi();
}
private static OkHttpClient createOkHttpClient() {
final OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient.addInterceptor(chain -> {
Request originalRequest = chain.request();
AccessToken accessToken= PreferencesHelper.getAccessToken(BaseApplication.getInstance());
String accessTokenStr = accessToken.getAccessToken();
Request.Builder builder =
originalRequest.newBuilder().header("Authorization", "Bearer " + accessTokenStr);
Request newRequest = builder.build();
return chain.proceed(newRequest);
});
return httpClient.build();
}
private static ApiService initApi(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(Constants._api_url)
.addConverterFactory(GsonConverterFactory.create())
.addConverterFactory(ScalarsConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(createOkHttpClient())
.build();
return retrofit.create(ApiService.class);
}
}
But we recently added Basic Auth, and now at the first request I get 401 and retryWhen() tries to update the Token, but still gets 401. That is, the doOnNext() does not work, but immediately the onError() works
private static Observable<AccessToken> refreshAccessToken() {
return RetrofitHelper.getApiService()
.getAccessToken(
Credentials.basic(
Constants._API_USERNAME, Constants._API_PASSWORD
),
Constants._API_BODY_USERNAME,
Constants._API_BODY_PASSWORD,
Constants._API_BODY_GRANT_TYPE
)
.doOnNext((AccessToken refreshedToken) -> {
PreferencesHelper.putObject(BaseApplication.getInstance(), PreferenceKey.ACCESS_TOKEN_KEY, refreshedToken);
}
});
}
// Api Service
public interface ApiService {
// Get Bearer Token
#FormUrlEncoded
#POST("oauth/token")
Observable<AccessToken> getAccessToken(#Header("Authorization") String basicAuth,
#Field("username") String username,
#Field("password") String password,
#Field("grant_type") String grantType);
}
Here, tell me why this is a mistake? Why at the first request I get 401, and from the second request everything works?
I want to suggest a better solution.
public class RefreshTokenTransformer<T extends Response<?>> implements ObservableTransformer<T, T> {
private class HttpCode {
private static final int UNAUTHORIZED_HTTP_CODE = 401;
}
private ApiService mApiService;
private UserRepository mUserRepository;
public RefreshTokenTransformer(ApiService service, UserRepository userRepository) {
mApiService = service;
mUserRepository = userRepository;
}
#Override
public ObservableSource<T> apply(final Observable<T> stream) {
return stream.flatMap(new Function<T, ObservableSource<T>>() {
#Override
public ObservableSource<T> apply(T response) throws Exception {
if (response.code() == HttpCode.UNAUTHORIZED_HTTP_CODE) {
return mApiService.refreshToken(mUserRepository.getRefreshTokenHeaders())
.filter(new UnauthorizedPredicate<>(mUserRepository))
.flatMap(new Function<Response<TokenInfo>, ObservableSource<T>>() {
#Override
public ObservableSource<T> apply(Response<TokenInfo> tokenResponse) throws Exception {
return stream.filter(new UnauthorizedPredicate<T>(mUserRepository));
}
});
}
return stream;
}
});
}
private class UnauthorizedPredicate<R extends Response<?>> implements Predicate<R> {
private UserRepository mUserRepository;
private UnauthorizedPredicate(UserRepository userRepository) {
mUserRepository = userRepository;
}
#Override
public boolean test(R response) throws Exception {
if (response.code() == HttpCode.UNAUTHORIZED_HTTP_CODE) {
throw new SessionExpiredException();
}
if (response.body() == null) {
throw new HttpException(response);
}
Class<?> responseBodyClass = response.body().getClass();
if (responseBodyClass.isAssignableFrom(TokenInfo.class)) {
try {
mUserRepository.validateUserAccess((TokenInfo) response.body());
} catch (UnverifiedAccessException error) {
throw new SessionExpiredException(error);
}
}
return true;
}
}
}
I`ve written the custom operator, which makes next actions:
first request started, and we get 401 response code;
then we execute /refresh_token request to update the token;
after that if the token is refreshed successfully, we repeat the
first request. if /refresh_token token is failed, we throw exception
Then, you can easy implement it in the any request like that:
Observable
.compose(new RefreshTokenResponseTransformer<Response<{$your_expected_result}>>
(mApiService, mUserRepository()));
One more important thing:
Most likely, that your initial observable for retrofit has params, like that:
mApiService.someRequest(token)
if the param is expected to change during the performing RefreshTokenTransformer(e.g. /refresh_token request will get new access token and you save it somewhere, then you want to use a fresh access token to repeat the request) you will need to wrap your observable with defer operator to force the creating of new observable like that:
Observable.defer(new Callable<ObservableSource<Response<? extends $your_expected_result>>>() {
#Override
public Response<? extends $your_expected_result> call() throws Exception {
return mApiService.someRequest(token);
}
})
I think it does not need to use interceptor instead you implement Authenticator by which you can access refreshed token and okhttp automatically will handle that. if you get 401 it updates header with refreshed token and make new request.
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();
}

How to Change API Base Url at Runtime(Retrofit,Android,Java)?

I need to change base url at run time.
I have login button and when login button click time i am called my login api
like below :
login api = http://192.168.0.61/api/authenticate
API_BASE_URL = http://192.168.0.61/api/
when i get success response from first api i get client server url for changing baseUrl.
CompanyUrlConfigEntity companyUrlConfigEntity = response.body();
like below :
String clientUrl = companyUrlConfigEntity.
getBaseUrl();
clientUrl = http://192.168.0.238/api/
In this project mainly for client or company based.So they have their own server.
Each company has using more than 20 api's.
So i need to change base url .
I am also checked below link for changing base url:
https://futurestud.io/tutorials/retrofit-2-how-to-change-api-base-url-at-runtime-2
and changed code like that
public static void changeApiBaseUrl(String newApiBaseUrl) {
API_BASE_URL = newApiBaseUrl;
builder = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new Gson()));
}
when i debugged and checked my baseUrl then it shows properly like below:
API_BASE_URL = http://192.168.0.238/api/
But when i call my customer api it shows the my first base url calling,
the url not changed.
expected customer api : http://192.168.0.238/api/customers
reality customer api : http://192.168.0.61/api/customers
I am also checked below link :
https://futurestud.io/tutorials/retrofit-2-how-to-use-dynamic-urls-for-requests
thats working , But each api need to pass fullPath url with each api like below:
#GET
public Call<ResponseBody> profilePicture(#Url String url);
But using this method , each api calling place i need to attach full path of url.
There is any other options? Please help me.
ServiceGenerator.class
public class ServiceGenerator {
public static String API_BASE_URL = "http://192.168.0.61/api/";
private static Retrofit retrofit;
private static OkHttpClient.Builder httpClient = new
OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new
Gson()));
private ServiceGenerator() {
}
public static void changeApiBaseUrl(String newApiBaseUrl) {
API_BASE_URL = newApiBaseUrl;
builder = new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(new NullOnEmptyConverterFactory())
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(GsonConverterFactory.create(new Gson()));
}
public static Retrofit retrofit() {
return retrofit;
}
public static <S> S createService(Class<S> serviceClass) {
return createService(serviceClass, null, null);
}
public static <S> S createService(Class<S> serviceClass,
final String authToken,
final ProgressListener progressListener) {
if (authToken != null) {
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
final String headerValue = AUTHORIZATION_TYPE + authToken;
Request request = original.newBuilder()
.header(AUTHORIZATION_HEADER_KEY, headerValue)
.method(original.method(), original.body())
.build();
return chain.proceed(request);
}
});
}
addResponseProgressListener(progressListener);
if (BuildConfig.DEBUG) {
HttpLoggingInterceptor httpLoggingInterceptor = new
HttpLoggingInterceptor();
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
httpClient.addInterceptor(httpLoggingInterceptor);
}
if (authToken != null) {
if (picasso == null) {
setUpPicasso(authToken);
}
}
OkHttpClient client = httpClient.build();
httpClient.connectTimeout(15, TimeUnit.SECONDS);
httpClient.readTimeout(2, TimeUnit.MINUTES);
httpClient.writeTimeout(2, TimeUnit.MINUTES);
retrofit = builder.client(client).build();
return retrofit.create(serviceClass);
}
}
LoginFragment.java
#OnClick(R.id.bt_login)
void onLogin() {
checkValidityOfUser();
}
private void checkValidityOfUser() {
final Login login = getLoginCredentials();
Call<CompanyUrlConfigEntity> callCheckValidity = dataProcessController.
getApiClient().
checkValidityOfUsers(login.getUsername());
callCheckValidity.enqueue(new Callback<CompanyUrlConfigEntity>() {
#Override
public void onResponse(Call<CompanyUrlConfigEntity> call,
Response<CompanyUrlConfigEntity> response) {
if (response.code() == 200) {
CompanyUrlConfigEntity companyUrlConfigEntity = response.body();
boolean status = companyUrlConfigEntity.isValidUser();
if (status) {
String baseUrls = companyUrlConfigEntity.
getBaseUrl();
baseUrls = baseUrls + "/api/";
ServiceGenerator.changeApiBaseUrl(baseUrls);
logins();
} else {
ToastHelper.show("please contact admin");
}
} else {
ToastHelper.show("" + response.code() + response.message());
}
}
#Override
public void onFailure(Call<CompanyUrlConfigEntity> call, Throwable t) {
ToastHelper.show("please contact admin");
}
});
}
private void logins() {
login = getLoginCredentials();
Call<Void> callLogin = dataProcessController.
getApiClient().
login(login);
callLogin.enqueue(new Callback<Void>() {
#Override
public void onResponse(Call<Void> call, Response<Void> response) {
if (response.code() == 200) {
} else if (response.code() == 401) {
}
}
#Override
public void onFailure(Call<Void> call, Throwable t) {
}
});
}
Base on your comments, I would say that you are correctly changing the API url on your builder, but that your second call still uses an instance of service where the url has not changed.
To explain a little more, from what I understand this is how everything gets executed:
when fragment is created, the apiClient is created and pointing to the first url
with dataProcessController.getApiClient() in your first call, you are getting the service that is pointing to the first url and then execute the call.
when the call is successful, you read the new url from result and update the ServiceGenerator with that new url. Then you execute the logins() method.
and in that method, you recall the dataProcessController.getApiClient() and do the second call with it. However, as you never redid apiClient = ServiceGenerator.createService(ApiClient.class);, the apiClient instance you are getting is still pointing to the first url, because it hasn't been notified that the url changed.
What I would try here, would be to change the method getApiClient() in your DataProcessController class to something like this:
public ApiClient getApiClient() {
apiClient = ServiceGenerator.createService(ApiClient.class);
return apiClient;
}
and see if this is work better.
Or if you don't want to regenerate the service inside that function, you can also do something like this:
public class DataProcessController {
private ApiClient apiClient = null;
private DataProcessController() {
regenerateClient();
}
public ApiClient getApiClient() { return apiClient; }
// add this to regenerate the client whenever url changes
public void regenerateClient() {
apiClient = ServiceGenerator.createService(ApiClient.class);
}
}
then, everytime you do change the url, do this:
ServiceGenerator.changeApiBaseUrl(baseUrls);
dataProcessController.regenerateClient();
and you should get a client that points to the correct url everytime you do dataProcessController.getApiClient()
https://segunfamisa.com/posts/firebase-remote-config
You should follow concept of firebase remote config. Here you dont need to store base Url in source code it will be retrieved from firebase config values which is stored at server of firebase.
// fetch
mRemoteConfig.fetch(3000)
.addOnCompleteListener(this, new OnCompleteListener<Void>() {
#Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
// update your base url here.
} else {
//task failed
}
}
});

Is it possible to use retrofit 1.9.0 and retrofit 2.x simultaneously?

I have an app with a lot of existing Retrofit 1.9 interfaces. I'd like to begin
upgrading to Retrofit 2.x incrementally (all at once is not currently feasable)
to gain support for RxJava call adapters (and because 1.9 is no longer being developed).
It was fairly trivial to get Retrofit1's RestAdapter to share an OkHttp3 client
that would be used in the Retrofit2 interfaces. Version 1.9 and 2.x also have
different maven groupIds, so the classes can exist side by side with no issues.
However, I get the following exception at runtime:
java.lang.IllegalAccessError: Method 'com.google.gson.stream.JsonWriter com.google.gson.Gson.newJsonWriter(java.io.Writer)' is inaccessible to class 'retrofit2.converter.gson.GsonRequestBodyConverter'
Retrofit 1 has a hard dependency on GSON 2.3.1, The method in question was made public in GSON 2.4. I've set my Gradle dependencies so that the GSON dependency resolves to v2.7 (the latest version as I post this):
build.gradle
compile('com.squareup.retrofit:retrofit:1.9.0') {
exclude module: 'gson'
}
compile 'com.jakewharton.retrofit:retrofit1-okhttp3-client:1.1.0'
compile "com.squareup.retrofit2:retrofit:2.3.0"
compile "com.squareup.retrofit2:converter-gson:2.3.0"
compile "com.squareup.retrofit2:adapter-rxjava:2.3.0"
compile 'com.google.code.gson:gson:2.7'
Running ./gradlew :app:dependencies indicates that GSON 2.7 is being resolved, however the runtime behavior is suspicious...
Update: I found that a 3rd party hardware SDK was bundling GSON 2.3.1 inside it's AAR. I can't figure out how to remove it though.
I recently implemented Retrofit 2.9.0 alongside Retrofit 1.9 because of version 2's ability to handle sessions is much better and one of my API calls was failing because of the lack of handling response cookies(the session).
I have the same issue where migrating the entire project to Retrofit 2 is not feasible at this time. I can confirm that it is working though.
I will show you snippets of how I implemented both 1.9 and 2.9.0. See bottom for a link to the full class.
For both:
Create a class from which you can access your Retrofit object and call interfaces from:
public class ApiManager {
private static final String TAG = "API MANAGER";
private static final String API_URL = BuildConfig.API_URL;
private static Gson gson = new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.setLenient()
.create();
// The rest of the class to follow
and then specific to 1.9:
private static RequestInterceptor requestInterceptor = new RequestInterceptor() {
#Override
public void intercept(RequestInterceptor.RequestFacade request) {
SessionManager sessionManager = new SessionManager(ContextHandler.getContext());
HashMap session = sessionManager.getUserDetails();
Object session_id = session.get("session_id");
Object token = session.get("token");
if (session_id != null && token != null) {
request.addHeader("Cookie", "session_id=" + session_id + ";");
request.addHeader("Cookie", "token=" + token + ";");
Log.i("INTERCEPT", "Sent Cookies");
}
request.addHeader("Accept", "application/json");
}
};
public static OkHttpClient getClient() {
// init okhttp 3 logger
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
JavaNetCookieJar jncj = new JavaNetCookieJar(CookieHandler.getDefault());
OkHttpClient client = new OkHttpClient();
client.newBuilder()
.addInterceptor(new AddCookiesInterceptor(ContextHandler.getContext()))
.addInterceptor(new ReceivedCookiesInterceptor(ContextHandler.getContext()))
.addNetworkInterceptor(logging)
.cookieJar(jncj)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.MINUTES);
return client;
}
private static final RestAdapter REST_ADAPTER = new RestAdapter.Builder()
.setEndpoint(API_URL) // On device
.setRequestInterceptor(requestInterceptor)
.setClient(new Ok3Client(getClient()))
.setConverter(new GsonConverter(gson))
.setLogLevel(RestAdapter.LogLevel.FULL) //log the request
.build();
public interface AuthenticationInterface {
#Headers("Content-type: application/json")
#POST("/auth/getsession")
void Authenticate(#Body Authentication Auth, Callback<SessionStore> response);
#Headers("Content-type: application/json")
#GET("/auth/logout")
void logout(Callback<String> response);
#Headers("Content-type: application/json")
#GET("/auth/logout")
String logout();
}
// Bind REST_ADAPTER to Interface
public static final AuthenticationInterface AUTHENTICATION_INTERFACE = REST_ADAPTER.create(AuthenticationInterface.class);
// Use this when you want to run the request.
public static AuthenticationInterface getAuthenticationService(){ return AUTHENTICATION_INTERFACE; }
So you would use the above as follows:
ApiManager.getAuthenticationService().Authenticate(auth, new Callback<SessionStore>() {
#Override
public void success(SessionStore sessionStore, Response response) {
// Do somthing
}
#Override
public void failure(RetrofitError error) {
// Handle Error
}
});
And for 2.9.0:
public static OkHttpClient getHeader() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient okClient = new OkHttpClient.Builder()
.addInterceptor(interceptor)
.addInterceptor(new AddCookiesInterceptor(ContextHandler.getContext()))
.addInterceptor(new ReceivedCookiesInterceptor(ContextHandler.getContext()))
.cookieJar(cookieJar)
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.MINUTES)
.addNetworkInterceptor(
new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = null;
Log.d("--Authorization-- ", "authorizationValue");
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder();
SessionManager sessionManager = new SessionManager(ContextHandler.getContext());
HashMap session = sessionManager.getUserDetails();
Object session_id = session.get("session_id");
Object token = session.get("token");
if (session_id != null && token != null) {
requestBuilder.addHeader("Cookie", "session_id=" + session_id + ";");
requestBuilder.addHeader("Cookie", "token=" + token + ";");
Log.i("INTERCEPT", "Sent Cookies");
}
requestBuilder.addHeader("Accept", "application/json");
request = requestBuilder.build();
return chain.proceed(request);
}
})
.build();
return okClient;
}
private static final Retrofit REST_ADAPTER2 = new Retrofit.Builder()
.baseUrl(API_URL) // On device
.client(getHeader())
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
public interface JasperReportsInterface {
/**
*
* #param agent_id
* #param report_id
*/
#retrofit2.http.Headers("Content-type: application/json")
#retrofit2.http.GET("/agents/{agent_id}/reports/{report_id}/")
Call<Reports> GetAgentReportView(#retrofit2.http.Path("agent_id") String agent_id, #retrofit2.http.Path("report_id") String report_id);
/**
*
* #param agent_id
* #param report_id
*/
#retrofit2.http.Headers("Content-type: application/json")
#retrofit2.http.GET("/agents/{agent_id}/reports/{report_id}/jobs")
Call<Jobs> PollAgentReportData(#retrofit2.http.Path("agent_id") String agent_id, #retrofit2.http.Path("report_id") String report_id);
/**
*
* #param agent_id
* #param report_id
* #param jsonBody
*/
#retrofit2.http.Headers("Content-type: application/json")
#retrofit2.http.POST("/agents/{agent_id}/reports/{report_id}/jobs")
Call<String> PostAgentReportData(#retrofit2.http.Path("agent_id") String agent_id, #retrofit2.http.Path("report_id") String report_id, #retrofit2.http.Body JsonObject jsonBody);
/**
*
* #param agent_id
* #param report_id
* #param jsonBody
*/
#retrofit2.http.Headers("Content-type: application/json")
#retrofit2.http.POST("/agents/{agent_id}/reports/{report_id}/jobs")
Call<String> DownloadAgentReportData(#retrofit2.http.Path("agent_id") String agent_id, #retrofit2.http.Path("report_id") String report_id, #retrofit2.http.Body JsonObject jsonBody);
}
// Bind REST_ADAPTER2 to Interface
public static final JasperReportsInterface JASPER_REPORTS_INTERFACE = REST_ADAPTER2.create(JasperReportsInterface.class);
// Use this when you want to run the request.
public static JasperReportsInterface getJasperReportsService(){ return JASPER_REPORTS_INTERFACE; }
And you would use the above as follows:
Call<Reports> reportsCall = ApiManager.getJasperReportsService().GetAgentReportView(agentsID, reportTypeID);
reportsCall.enqueue(new retrofit2.Callback<Reports>() {
#Override
public void onResponse(Call<Reports> call, retrofit2.Response<Reports> response) {
if(response.isSuccessful()) {
report = response.body();
} else {
int statusCode = response.code();
// handle request errors yourself
ResponseBody errorBody = response.errorBody();
}
}
#Override
public void onFailure(Call<Reports> call, Throwable t) {
}
});
Dependencies you would need are the basic ones need for both 1.9 and 2 respectively.
See here for full class.

Using Retrofit in Android

I have an android app that has 3 activities :
A login activity
A tasks acivity where all tasks pertaining to a user are displayed (Populated using an Array Adapter)
A task_details activity which results from clicking a task on the list
I have to consume REST Apis. The research I have done so far directs me to use Retrofit. I checked how to use it and found out that :
Set the base URL in the Main Activity (Mine is the Login Activity)
I need to create a API class and define my functions using annotations.
Use the class Rest Adapter in the Activity and define Callbacks.
Had my app been a single activity app, I would have crunched everything in my MainActivity.java but I don't know how and where to put all the code from steps 1,2,3 for use in my 3 activities.Could you please help by telling how to use Retrofit in my app. Thanks a lot.
Specifically, I need network calls to :
1. Login the user
2. Get all the tasks of the user.
And for both I would be using a given REST api.
*********************************************
Calling Api USing Retrofit
*********************************************
**Dependancies** :-
implementation 'com.android.support:recyclerview-v7:27.1.1'
implementation 'com.squareup.picasso:picasso:2.5.2'
implementation 'com.android.support:cardview-v7:27.1.1'
enter code here
**Model**
use the Pozo class
**Api Call**
-> getLogin() // use the method
//API call for Login
private void getLogin()
{
getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
AsyncHttpClient client = new AsyncHttpClient();
RequestParams requestParams = new RequestParams();
requestParams.put("email_id", edit_email.getText().toString());
requestParams.put("password", edit_password.getText().toString());
Log.e("", "LOGIN URL==>" + Urls.LOGIN + requestParams);
Log.d("device_token", "Device_ Token" + FirebaseInstanceId.getInstance().getToken());
client.post(Urls.LOGIN, requestParams, new JsonHttpResponseHandler() {
#Override
public void onStart() {
super.onStart();
ShowProgress();
}
#Override
public void onFinish() {
super.onFinish();
Hideprogress();
}
#Override
public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
super.onSuccess(statusCode, headers, response);
Log.e("", "Login RESPONSE-" + response);
Login login = new Gson().fromJson(String.valueOf(response), Login.class);
edit_email.setText("");
edit_password.setText("");
if (login.getStatus().equals("true")) {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
MDToast mdToast = MDToast.makeText(SignInActivity.this, String.valueOf("User Login Successfully!"),
MDToast.LENGTH_SHORT, MDToast.TYPE_SUCCESS);
mdToast.show();
Utils.WriteSharePrefrence(SignInActivity.this, Util_Main.Constant.EMAIL, login.getData().getEmailId());
Utils.WriteSharePrefrence(SignInActivity.this, Constant.USERID, login.getData().getId());
Utils.WriteSharePrefrence(SignInActivity.this, Constant.USERNAME, login.getData().getFirstName());
Utils.WriteSharePrefrence(SignInActivity.this, Constant.PROFILE, login.getData().getProfileImage());
hideKeyboard(SignInActivity.this);
Intent intent = new Intent(SignInActivity.this, DashboardActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
startActivity(intent);
finish();
} else {
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
MDToast mdToast = MDToast.makeText(SignInActivity.this, String.valueOf("Login Denied"),
MDToast.LENGTH_SHORT, MDToast.TYPE_ERROR);
mdToast.show();
}
}
#Override
public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
super.onFailure(statusCode, headers, responseString, throwable);
Log.e("", throwable.getMessage());
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
MDToast mdToast = MDToast.makeText(SignInActivity.this, "Something went wrong",
MDToast.LENGTH_SHORT, MDToast.TYPE_ERROR);
mdToast.show();
}
});
}
Using Retrofit is quite simple and straightforward.
First of all you need to add retrofit to your project, as example with Gradle build sytem.
compile 'com.squareup.retrofit:retrofit:1.7.1' |
another way you can download .jar and place it to your libs folder.
Then you need to define interfaces that will be used by Retrofit to make API calls to your REST endpoints. For example for users:
public interface YourUsersApi {
//You can use rx.java for sophisticated composition of requests
#GET("/users/{user}")
public Observable<SomeUserModel> fetchUser(#Path("user") String user);
//or you can just get your model if you use json api
#GET("/users/{user}")
public SomeUserModel fetchUser(#Path("user") String user);
//or if there are some special cases you can process your response manually
#GET("/users/{user}")
public Response fetchUser(#Path("user") String user);
}
Ok. Now you have defined your API interface an you can try to use it.
To start you need to create an instance of RestAdapter and set base url of your API back-end. It's also quite simple:
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint("https://yourserveraddress.com")
.build();
YourUsersApi yourUsersApi = restAdapter.create(YourUsersApi.class);
Here Retrofit will read your information from interface and under the hood it will create RestHandler according to meta-info your provided which actually will perform HTTP requests.
Then under the hood, once response is received, in case of json api your data will be transformed to your model using Gson library so you should be aware of that fact that limitations that are present in Gson are actually there in Retrofit.
To extend/override process of serialisers/deserialisation your response data to your models you might want to provide your custom serialisers/deserialisers to retrofit.
Here you need to implement Converter interface and implement 2 methods fromBody() and toBody().
Here is example:
public class SomeCustomRetrofitConverter implements Converter {
private GsonBuilder gb;
public SomeCustomRetrofitConverter() {
gb = new GsonBuilder();
//register your cursom custom type serialisers/deserialisers if needed
gb.registerTypeAdapter(SomeCutsomType.class, new SomeCutsomTypeDeserializer());
}
public static final String ENCODING = "UTF-8";
#Override
public Object fromBody(TypedInput body, Type type) throws ConversionException {
String charset = "UTF-8";
if (body.mimeType() != null) {
charset = MimeUtil.parseCharset(body.mimeType());
}
InputStreamReader isr = null;
try {
isr = new InputStreamReader(body.in(), charset);
Gson gson = gb.create();
return gson.fromJson(isr, type);
} catch (IOException e) {
throw new ConversionException(e);
} catch (JsonParseException e) {
throw new ConversionException(e);
} finally {
if (isr != null) {
try {
isr.close();
} catch (IOException ignored) {
}
}
}
}
#Override
public TypedOutput toBody(Object object) {
try {
Gson gson = gb.create();
return new JsonTypedOutput(gson.toJson(object).getBytes(ENCODING), ENCODING);
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
private static class JsonTypedOutput implements TypedOutput {
private final byte[] jsonBytes;
private final String mimeType;
JsonTypedOutput(byte[] jsonBytes, String encode) {
this.jsonBytes = jsonBytes;
this.mimeType = "application/json; charset=" + encode;
}
#Override
public String fileName() {
return null;
}
#Override
public String mimeType() {
return mimeType;
}
#Override
public long length() {
return jsonBytes.length;
}
#Override
public void writeTo(OutputStream out) throws IOException {
out.write(jsonBytes);
}
}
}
And now you need to enable your custom adapters, if it was needed by using setConverter() on building RestAdapter
Ok. Now you are aware how you can get your data from server to your Android application. But you need somehow mange your data and invoke REST call in right place.
There I would suggest to use android Service or AsyncTask or loader or rx.java that would query your data on background thread in order to not block your UI.
So now you can find the most appropriate place to call
SomeUserModel yourUser = yourUsersApi.fetchUser("someUsers")
to fetch your remote data.
I have just used retrofit for a couple of weeks and at first I found it hard to use in my application. I would like to share to you the easiest way to use retrofit in you application. And then later on if you already have a good grasp in retrofit you can enhance your codes(separating your ui from api and use callbacks) and maybe get some techniques from the post above.
In your app you have Login,Activity for list of task,and activity to view detailed task.
First thing is you need to add retrofit in your app and theres 2 ways, follow #artemis post above.
Retrofit uses interface as your API. So, create an interface class.
public interface MyApi{
/*LOGIN*/
#GET("/api_reciever/login") //your login function in your api
public void login(#Query("username") String username,#Query("password") String password,Callback<String> calback); //this is for your login, and you can used String as response or you can use a POJO, retrofit is very rubust to convert JSON to POJO
/*GET LIST*/
#GET("/api_reciever/getlist") //a function in your api to get all the list
public void getTaskList(#Query("user_uuid") String user_uuid,Callback<ArrayList<Task>> callback); //this is an example of response POJO - make sure your variable name is the same with your json tagging
/*GET LIST*/
#GET("/api_reciever/getlistdetails") //a function in your api to get all the list
public void getTaskDetail(#Query("task_uuid") String task_uuid,Callback<Task> callback); //this is an example of response POJO - make sure your variable name is the same with your json tagging
}
Create another interface class to hold all your BASE ADDRESS of your api
public interface Constants{
public String URL = "www.yoururl.com"
}
In your Login activity create a method to handle the retrofit
private void myLogin(String username,String password){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constants.URL) //call your base url
.build();
MyApi mylogin = restAdapter.create(MyApi.class); //this is how retrofit create your api
mylogin.login(username,password,new Callback<String>() {
#Override
public void success(String s, Response response) {
//process your response if login successfull you can call Intent and launch your main activity
}
#Override
public void failure(RetrofitError retrofitError) {
retrofitError.printStackTrace(); //to see if you have errors
}
});
}
In your MainActivityList
private void myList(String user_uuid){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constants.URL) //call your base url
.build();
MyApi mytask = restAdapter.create(MyApi.class); //this is how retrofit create your api
mytask.getTaskDetail(user_uuid,new Callback<Task>>() {
#Override
public void success(ArrayList<Task> list, Response response) {
//process your response if successful load the list in your listview adapter
}
#Override
public void failure(RetrofitError retrofitError) {
retrofitError.printStackTrace(); //to see if you have errors
}
});
}
In your Detailed List
private void myDetailed(String task_uuid){
RestAdapter restAdapter = new RestAdapter.Builder()
.setEndpoint(Constants.URL) //call your base url
.build();
MyApi mytask = restAdapter.create(MyApi.class); //this is how retrofit create your api
mytask.getTaskList(task_uuid,new Callback<Task>() {
#Override
public void success(Task task, Response response) {
//process your response if successful do what you want in your task
}
#Override
public void failure(RetrofitError retrofitError) {
retrofitError.printStackTrace(); //to see if you have errors
}
});
}
Hope this would help you though its really the simplest way to use retrofit.
Take a look at this , excellent blog on using Retrofit in conjunction with Otto, both libraries are from Square.
http://www.mdswanson.com/blog/2014/04/07/durable-android-rest-clients.html
The basic idea is that you will hold a reference to a "repository" object in your Application class. This object will have methods that "subscribe" to rest api event requests. When one is received it will make the proper Retrofit call, and then "post" the response, which can then be "subscribed" to by another component (such as the activity that made the request).
Once you have this all setup properly, accessing data via your rest api becomes very easy. For example, making are request for data would look something like this :
mBus.post(new GetMicropostsRequest(mUserId));
and consuming the data would look something like this:
#Subscribe
public void onGetUserProfileResponse(GetUserProfileResponse event) {
mView.setUserIcon("http://www.gravatar.com/avatar/" + event.getGravatar_id());
mView.setUserName(event.getName());
}
It takes a little bit of upfront effort, but in the end it becomes "trivial" to access anything you need from our backend via Rest.
You may try saving references to your api inside your application class. Then you can get it's instance from any activity or fragment and get api from there. That sounds a little weird, but it may be a simple DI alternative. And if you will only store references in your app class, it won't be a kind of god object
UPD: http://square.github.io/retrofit/ - here is some documentation, it might be useful
Using RetroFit is very easy.
Add dependecy in build.gradle.
compile 'com.squareup.retrofit:retrofit:1.9.0'
compile 'com.squareup.okhttp:okhttp:2.4.0'
Make an Interface for all http methods.
Copy your json output and create pojo class to recieve json of your
response, you can make pojo from JsonSchema2pojo site .
make an adapter and call your method
for complete demo try this tutorial Retrofit Android example
Checkout this app that demonstrates Retrofit integration to Google Tasks API.
https://github.com/sschendel/SyncManagerAndroid-DemoGoogleTasks
There are examples of Retrofit api (TaskApi) used within Activity AsyncTask in MainActivity, as well as examples of use within Sync Adapter in background service.
The strategy from the article posted in #nPn's answer is probably a more elegant solution, but you can at least look at another working example.
Firstly, putting everything in MainActivity would be bad practice and you would end up with a God object.
The documentation on the Retrofit site is fantastic, so I'm going to read your question on how to structure the project. I wrote a very small app for demonstration purposes. It loads cats from the cat API and should be fairly simple to follow what is happening.
It has an example of using JSON or XML for the parsing of data from the service. You can find it at https://github.com/codepath/android_guides/wiki/Consuming-APIs-with-Retrofit
Hopefully you can extrapolate why I have structured it the way I have. I'm happy to answer any questions you have in the comments and update the answer.
I find these tutorials AndroidHive , CodePath helpful
I will briefly describe what I have learned.
Step 1 : Add these three dependencies to build.gradle and Add Internet permission to Manifest
compile 'com.google.code.gson:gson:2.6.2' // for string to class conversion. Not Compulsory
compile 'com.squareup.retrofit2:retrofit:2.1.0'// compulsory
compile 'com.squareup.retrofit2:converter-gson:2.1.0' //for retrofit conversion
Add them in Manifest
<uses-permission android:name="android.permission.INTERNET" />
Step 2
Creae ApiClient and ApiInterface.
public class ApiClient {
public static final String BASE_URL = "http://yourwebsite/services/";
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;
}
}
where ApiInterface.class
public interface ApiInterface {
// getting same data in three different ways.
#GET("GetCompanyDetailByID")
Call<CompanyResponse> getDetailOfComapanies(#Query("CompanyID") int companyID);
#GET("GetCompanyDetailByID")
Call<ResponseBody> getRawDetailOfCompanies(#Query("CompanyID") int companyID);
#GET("{pathToAdd}")
Call<CompanyResponse> getDetailOfComapaniesWithPath(#Path("pathToAdd") String pathToAppend, #Query("CompanyID") int companyID);
}
And call this service like
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<CompanyResponse> companyResponseCall = apiService.getDetailOfComapanies(2);
//Call<CompanyResponse> companyResponseCall = apiService.getDetailOfComapaniesWithPath("GetCompanyDetailByID",2);
companyResponseCall.enqueue(new Callback<CompanyResponse>() {
#Override
public void onResponse(Call<CompanyResponse> call, Response<CompanyResponse> response) {
CompanyResponse comapnyResponse = response.body();
Boolean status = comapnyResponse.getStatus();
}
#Override
public void onFailure(Call<CompanyResponse> call, Throwable t) {
}
});
For Getting Raw Json String
Call<ResponseBody> call = apiService.getRawDetailOfCompanies(2);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
String jsonStr = response.body().string();
if(!jsonStr.isEmpty()){
Gson gson = new Gson();
JSONObject jObject = new JSONObject(jsonStr).getJSONObject("data");
//1st Method
Data dataKiType = gson.fromJson(jObject.toString(), Data.class);
dataKiType.getCompanyDetail();
//2nd method for creaing class or List at runTime
Type listType = new TypeToken<Data>(){}.getType();
Data yourClassList = new Gson().fromJson(jObject.toString(), listType);
yourClassList.getCompanyDetail();
} e.printStackTrace();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
}
});
You can create your business object using http://www.jsonschema2pojo.org/ by simply pasting json. and selecting source type to JSON and Annotation Style to GSon
Found a small, but complete and concise example at
https://github.com/square/retrofit/tree/master/samples
Beginners find it little intimidating to learn retrofit. I have prepared a tutorial which will simplify the learning curve. See Retrofit android tutorial for more information.
Developing your own type-safe HTTP library to interface with a REST API can be a real pain: you have to handle many aspects, such as making connections, caching, retrying failed requests, threading, response parsing, error handling, and more. Retrofit, on the other hand, is a well-planned, documented and tested library that will save you a lot of precious time and headaches.
compile 'com.google.code.gson:gson:2.6.2'
compile 'com.squareup.retrofit2:retrofit:2.1.0'// compulsory
compile 'com.squareup.retrofit2:converter-gson:2.1.0' //for retrofit conversion
First, add this lines to gradle file
compile 'com.squareup.retrofit2:retrofit:2.1.0'
compile 'com.squareup.retrofit2:converter-gson:2.1.0'
compile 'com.google.code.gson:gson:2.7'
compile 'com.squareup:otto:1.3.8'
compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
Then Create Objects in OnCreate of Activity
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client= new OkHttpClient
.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.addInterceptor(interceptor).build();
Gson gson=new GsonBuilder()
.setDateFormat("yyyy-MM-dd'T'HH:mm:ssZ")
.create();
Retrofit retrofit= new Retrofit.Builder()
.baseUrl("url")
.client(client)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
Create an iterface
public interface summaryListAPI {
//post
#FormUrlEncoded
#POST("index.php")
Call<summaryList> post(
#Field("status") String status,
#Field("sox") String sox
);
//get
#GET("yesbdeChatHistoryList/{userId}/")
Call<List<ChatTabTwoResp>> getFriends(
#Path("userId") int userId
);
}
Create classes
public class summaryList {
#SerializedName("bookingSummary") #Expose private List<summaryListData> status = new ArrayList<summaryListData>();
}
public class summaryListData {
#SerializedName("date") #Expose private String date;
}
Add this Method to your activity
public void apiSummaryListMain(final Retrofit retrofit) {
retrofit.create(summaryListAPI.class).post("8547861657","100").enqueue(new Callback<summaryList>() {
#Override
public void onResponse(Call<summaryList> call, Response<summaryList> response) {
if (response.isSuccessful()) {
progressBar.setVisibility(View.INVISIBLE);
List<summaryListData> summary_List= response.body().getStatus();
}else{
}
}
#Override
public void onFailure(Call<summaryList> call, Throwable t) {
}
});
}
Its Working
package com.keshav.gmailretrofitexampleworking.network;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;
public class ApiClient {
public static final String BASE_URL = "http://api.androidhive.info/json/";
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;
}
}
==============================================
package com.keshav.gmailretrofitexampleworking.network;
import com.keshav.gmailretrofitexampleworking.models.Message;
import java.util.List;
import retrofit2.Call;
import retrofit2.http.GET;
public interface ApiInterface {
#GET("inbox.json")
Call<List<Message>> getInbox();
}
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'
=====================================================
Call Retrofit 2 APi inside onCreate
private void getInbox() {
swipeRefreshLayout.setRefreshing(true);
ApiInterface apiService =
ApiClient.getClient().create(ApiInterface.class);
Call<List<Message>> call = apiService.getInbox();
call.enqueue(new Callback<List<Message>>() {
#Override
public void onResponse(Call<List<Message>> call, Response<List<Message>> response) {
// clear the inbox
messages.clear();
// add all the messages
// messages.addAll(response.body());
// TODO - avoid looping
// the loop was performed to add colors to each message
Log.e("keshav","response" +response.body());
for (Message message : response.body()) {
// generate a random color
// TODO keshav Generate Random Color Here
message.setColor(getRandomMaterialColor("400"));
messages.add(message);
}
mAdapter.notifyDataSetChanged();
swipeRefreshLayout.setRefreshing(false);
}
#Override
public void onFailure(Call<List<Message>> call, Throwable t) {
Toast.makeText(getApplicationContext(), "Unable to fetch json: " + t.getMessage(), Toast.LENGTH_LONG).show();
swipeRefreshLayout.setRefreshing(false);
}
});
}
Source Code
https://drive.google.com/open?id=0BzBKpZ4nzNzUVFRnVVkzc0JabUU
https://drive.google.com/open?id=0BzBKpZ4nzNzUc2FBdW00WkRfWW8
I just braked this problem in a very easy way you just need install a plugin and follow some steps to implement retrofit in any of your App.:
Already posted answer : Retrofit in android?
Add (QAssist - Android Studio Plugin) Android plugin in your Android studio. ( https://github.com/sakkeerhussain/QAssist ).
Hope this will help you.
Simple retrofit + okhttp integration using RxJava
public WebService apiService(Context context) {
String mBaseUrl = context.getString(BuildConfig.DEBUG ? R.string.local_url : R.string.live_url);
int cacheSize = 5 * 1024 * 1024; // 5 MB
Cache cache = new Cache(context.getCacheDir(), cacheSize);
HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(BuildConfig.DEBUG ? HttpLoggingInterceptor.Level.BODY : HttpLoggingInterceptor.Level.NONE);
OkHttpClient okHttpClient = new OkHttpClient.Builder()
.readTimeout(120, TimeUnit.SECONDS)
.writeTimeout(120, TimeUnit.SECONDS)
.connectTimeout(120, TimeUnit.SECONDS)
.addInterceptor(loggingInterceptor)
//.addNetworkInterceptor(networkInterceptor)
.cache(cache)
.build();
return new Retrofit.Builder().baseUrl(mBaseUrl)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build().create(WebService.class);
}
public interface APIService {
#POST(Constant.updateProfile)
#FormUrlEncoded
Call<ResponseBody> updateProfile(
#Field("user_id") String user_id,
#Field("first_name") String first_name,
#Field("last_name") String last_name
);
}
public class RetrofitClient {
private static Retrofit retrofit = null;
public static Retrofit getClient(String baseUrl) {
if (retrofit == null) {
retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit;
}
}
public class Body {
// Check Status if Status True or False
String status;
String message;
String checksum;
}
public interface OnBodyResponseListner {
public void onSucces(Body response);
public void onFailure(Body response);
public void onBlankBody(Call<ResponseBody> call);
}
public static void setOnWebServiceCallListner(final Call<ResponseBody> t, final OnBodyResponseListner onBodyResponseListner) {
t.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
try {
call.cancel();
Gson gson = new GsonBuilder().serializeNulls().create();
String json = response.body().string();
Log.d(TAG, json + " ~ Response ~ " + json);
Body body = gson.fromJson(json, Body.class);
if (body.getStatus().equalsIgnoreCase("true")) {
onBodyResponseListner.onSucces(body);
} else {
onBodyResponseListner.onFailure(body);
}
} catch (Exception e) {
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
onBodyResponseListner.onBlankBody(call);
Log.d(TAG, "~ Response Message Blank ~ " + t.getMessage() + " \n Localize Message ~ " + t.getLocalizedMessage() + " \n" + t.getStackTrace().toString());
}
});
}
APIService mService = RetrofitClient.getClient(Constant.BASE_URL).create(APIService.class);
Oprations.setOnWebServiceCallListner(mService.updateProfile("12",
"first_name",
"last,name"
), new OnBodyResponseListner() {
#Override
public void onSucces(Body response) {
}
#Override
public void onFailure(Body response) {
Toast.makeText(mContext, response.getMessage(), Toast.LENGTH_SHORT).show();
}
#Override
public void onBlankBody(Call<ResponseBody> call) {
}
});
}
First, add the dependency - compile 'com.squareup.retrofit:retrofit:1.7.1'
Create retrofit instance class
public class RetrofitClientInstance {
private Context context;
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit retrofit
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(BuildConfig.SERVER_URL)
.client(getRequestHeader())
.addConverterFactory(GsonConverterFactory.create());
public static HttpLoggingInterceptor setLogger() {
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
return logging;
}
public static <S> S createService(Class<S> serviceClass) {
if (!httpClient.interceptors().isEmpty()) {
httpClient.interceptors().clear();
}
httpClient.authenticator(new AccessTokenAuthenticator());
httpClient.readTimeout(60,TimeUnit.SECONDS);
httpClient.connectTimeout(60,TimeUnit.SECONDS);
httpClient.addInterceptor(setLogger());
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request original = chain.request();
Request.Builder requestBuilder = original.newBuilder()
.header("LocalSystemDate", MyApplication.getInstance().getCurrentDateAndTimeWithTimeZone())
.addHeader("channel_id", AppConst.CHANNEL_ID)
.addHeader("Authorization", "Bearer" + " " + SharedPref.getsAccessToken())
.method(original.method(), original.body());
Request request = requestBuilder.build();
return chain.proceed(request);
}
});
OkHttpClient client = httpClient.build();
Retrofit retrofit = builder.client(client).build();
return retrofit.create(serviceClass);
}
private static OkHttpClient getRequestHeader() {
return new OkHttpClient.Builder()
.readTimeout(60, TimeUnit.SECONDS)
.connectTimeout(60, TimeUnit.SECONDS)
.build();
}
}
Write the interface
public interface GetDataService {
#Headers("Content-Type: application/json")
#POST("login")
Call<LoginResponseModel> loginUser(
#Body LoginBodyModel loginBodyModel
);
}
Call the service
GetDataService service = RetrofitClientInstance.createService(GetDataService.class);
Call<LoginResponseModel> loginModelCall = service.loginUser(loginBodyModel);
loginModelCall.enqueue(new Callback<LoginResponseModel>() {
#Override
public void onResponse(Call<LoginResponseModel> call, Response<LoginResponseModel> response) {
}
#Override
public void onFailure(Call<LoginResponseModel> call, Throwable t) {
}
});
You can do this with hilt
#Module
#InstallIn(SingletonComponent::class)
object RestClientModule {
#Provides
#Singleton
internal fun provideApiService(
#WeatherRetrofit retrofit: Retrofit
): ApiService = retrofit.create(ApiService::class.java)
#Provides
#WeatherRetrofit
#Singleton
internal fun provideHiCityRetrofit(
okHttpClient: OkHttpClient,
moshi: Moshi
): Retrofit = Retrofit.Builder()
.baseUrl(EndPoint.STAGE_BASE_URL)
.client(okHttpClient)
.addConverterFactory(ScalarsConverterFactory.create())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
}
You can checkout this Github Link with source-code avaible for login and signup API in android. https://github.com/probelalkhan/kotlin-retrofit-tutorial/commits/master
Or you can check this tutorial for learning purpose.
https://youtu.be/TyJEDhauUeQ
Or for more other details checkout this
https://gist.github.com/codinginflow/d7a02bd69eebf566b4650c41bc362be7

Categories

Resources