I am using android as my front-end application and Spring boot as server part. I am using android retrofit library to connect with server.
When user logs onto server, he gets response like this.
So how do I extract "accessToken" and "tokenType" from body response?
Here is my login method in android:
private void login(LoginRequest loginRequest) {
OkHttpClient.Builder okHttpClientBuilder = new OkHttpClient.Builder();
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
okHttpClientBuilder.addInterceptor(logging);
Retrofit.Builder builder = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.client(okHttpClientBuilder.build());
Retrofit retrofit = builder.build();
RestAPI client = retrofit.create(RestAPI.class);
Call<LoginRequest> call = client.signIn(loginRequest);
call.enqueue(new Callback<LoginRequest>() {
#Override
public void onResponse(Call<LoginRequest> call, Response<LoginRequest> response) {
if (response.code() == 200) {
Toast.makeText(getApplicationContext(), response.body().toString(),
Toast.LENGTH_LONG).show();
Intent i = new Intent(LoginActivity.this, PostsActivity.class);
//response.body should be somewhere here
startActivity(i);
}else{
Toast.makeText(getApplicationContext(), "Uneti podaci nisu dobri",
Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<LoginRequest> call, Throwable t) {
}
});
}
This is my LoginRequest class
public class LoginRequest {
private String username;
private String password;
public LoginRequest(String username, String password) {
this.username = username;
this.password = password;
}
}
Create model class for response like below
public class Token {
#SerializedName("tokenType")
private String tokenType;
#SerializedName("accessToken")
private String accessToken;
public String getTokenType() {
return tokenType;
}
public void setTokenType(String tokenType) {
this.tokenType = tokenType;
}
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
}
Then change the Api return type to Call<Token>
Accordingly you also need to modify the call
call.enqueue(new Callback<Token>() {
#Override
public void onResponse(Call<Token> call, Response<Token> response) {
if(response.isSuccessful()) {
Token token = response.body();
}
}
#Override
public void onFailure(Call<Token> call, Throwable t) {
}
});
Related
I'm using retrofit2 for password recovery, an API request is sent to the server with an email address which is entered by the user.
I'm only setting the email in the POJO ,and after the conversion, the JSON string looks like this:
{"email":"email#email.com", "password":"", "password_confirmation":"", "token":""}
But the JSON I need to send out, should look like this:
{"email":"email#email.com"}
If I'll create another POJO class with an email param then I'll get the required string, but I just want to know if it is possible, using the current POJO.
How would one convert a POJO object to a JSON string using gson for a specified field?
Please Refer to the below code:
public class User {
private String email;
private String password;
private String password_confirmation;
private String token;
public User() {
this.email="";
this.password="";
this.password_confirmation="";
this.token="";
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPassword_confirmation() {
return password_confirmation;
}
public void setPassword_confirmation(String password_confirmation) {
this.password_confirmation = password_confirmation;
}
public String getToken() {
return token;
}
public void setToken(String token) {
this.token = token;
}
}
My Api interface
public interface Api{
#POST("/api/password/create")
#Headers({"Accept:application/json", "Content-Type:application/json"})
Call<User> Create(#Body RequestBody requestBody);
}
My Retrofit method
private void authenticateEmail(final Context context) {
User user=new User();
Api api=new Api();
String email = edt_forgot_email.getText().toString().trim();
Gson gson = new GsonBuilder()
.setDateFormat(SERVICE_DATE_FORMAT)
.setLenient()
.create();
api = new Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create(gson))
.build();
user.setEmail(email);
Gson lGson = new GsonBuilder().setDateFormat(SERVICE_DATE_FORMAT).create();
String jsonString = lGson.toJson(user);
Log.d("debug", "jsonString==>" + jsonString);
final RequestBody requestBody = RequestBody.create(MediaType.parse("application/json"), jsonString);
Call<User> call = api.Create(requestBody);
Log.d("debug", "url: " + call.request().url().toString());
call.enqueue(new Callback<User>() {
#Override
public void onResponse(#NonNull Call<User> call, #NonNull Response<User> response) {
Log.d("debug", "responsecode==>" + response.code());
Log.d("debug", "responsebody==>" + response.body());
if (response.code() == 200) {
String msg = "We have emailed you OTP";
Toast.makeText(activity, msg, Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NonNull Call<User> call, #NonNull Throwable t) {
Toast.makeText(activity, "server error",Toast.LENGTH_SHORT).show();
}
});
}
Remove this code from constructor :
public User() {
this.email="";
this.password="";
this.password_confirmation="";
this.token="";
}
If you want to exclude empty values from output json, you should make it as null.
Good luck!
You can just create a JSON Object for the single field like this:
JSONObject data =new JSONObject();
data.put("email", email#email.com)
send data object to the retrofit.
I'm doing a side project and all I know is I should use Basic Authentication. Since I don't have experience using it, I found some stuff online and I want to ask you for your opinion, is this the right way to do it...
So, first thing is from Retrofit's documentation:
This is the Retrofit(network) setup:
public class RetrofitSetup {
public static final String API_BASE_URL = "----";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
private static Retrofit retrofit = builder.build();
public static <S> S createService(Class<S> serviceClass) {
return createService(serviceClass, null, null);
}
public static <S> S createService(
Class<S> serviceClass, String username, String password) {
if (!TextUtils.isEmpty(username)
&& !TextUtils.isEmpty(password)) {
String authToken = Credentials.basic(username, password);
return createService(serviceClass, authToken);
}
return createService(serviceClass, null);
}
public static <S> S createService(
Class<S> serviceClass, final String authToken) {
if (!TextUtils.isEmpty(authToken)) {
AuthenticationInterceptor interceptor =
new AuthenticationInterceptor(authToken);
if (!httpClient.interceptors().contains(interceptor)) {
httpClient.addInterceptor(interceptor);
builder.client(httpClient.build());
retrofit = builder.build();
}
}
return retrofit.create(serviceClass);
}
}
And Authentication Interceptor:
public class AuthenticationInterceptor implements Interceptor {
private String authToken;
public AuthenticationInterceptor(String token) {
this.authToken = token;
}
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request original = chain.request();
Request.Builder builder = original.newBuilder()
.header("Authorization", authToken);
Request request = builder.build();
return chain.proceed(request);
}
}
This is my RetrofitSerive class:
public interface RetrofitService {
#GET("login")
Call<Void> basicLogin();
#GET("contact")
Call<List<Contacts>> getContacts(#Header("Authorization") String authkey);
#GET("product")
Call<List<Products>> getProducts(#Header("Authorization") String authkey);
}
And the class I found online for generating auth key:
public class Helper {
public static String getAuthToken(String username, String password) {
byte[] data = new byte[0];
try {
data = (username + ":" + password).getBytes("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
return "Failed to authenticate";
}
return "Basic " + Base64.encodeToString(data, Base64.NO_WRAP);
}
}
And I believe there is nothing to be changed here... So first thing is to login (check authentication), here is the couple of stuff I want to know how to do right:
private void login(final String username, final String password) {
RetrofitService loginService =
RetrofitSetup.createService(RetrofitService.class, username, password);
Call<Void> call = loginService.basicLogin();
call.enqueue(new Callback<Void>() {
#Override
public void onResponse(#NonNull Call<Void> call, #NonNull Response<Void> response) {
progressBar.setVisibility(View.GONE);
if (response.isSuccessful()) {
// user object available
editor.putString("username", username);
editor.putString("password", password);
editor.apply();
startActivity(new Intent(LoginActivity.this, MainActivity.class));
finish();
} else {
Toast.makeText(LoginActivity.this, response.message(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NonNull Call<Void> call, #NonNull Throwable t) {
progressBar.setVisibility(View.GONE);
Toast.makeText(LoginActivity.this, t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
If response is successfull, I save those credentials in SharedPreferences..
Next page shuld call another request, so I wonder if there is any difference if I send both username/password or auth key?
This is how I did it:
private void getContacts() {
prefs = getActivity().getSharedPreferences(KEY, MODE_PRIVATE);
String username = prefs.getString("username", null);
String password = prefs.getString("password", null);
RetrofitService loginService =
RetrofitSetup.createService(RetrofitService.class, Helper.getAuthToken(username, password));
Call<List<Contacts>> call = loginService.getContacts(Helper.getAuthToken(username, password));
call.enqueue(new Callback<List<Contacts>>() {
#Override
public void onResponse(#NonNull Call<List<Contacts>> call, #NonNull Response<List<Contacts>> response) {
if (response.isSuccessful()) {
kontaktiAdapter.setKontakti(response.body());
} else {
Toast.makeText(getActivity(), response.message(), Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(#NonNull Call<List<Contacts>> call, #NonNull Throwable t) {
Toast.makeText(getActivity(), t.getMessage(), Toast.LENGTH_SHORT).show();
}
});
}
So in this call, instead of sending username/password to RetrofitSetup.createService, I'm sending Helper.getAuthToken(username, password) with username and password from SharedPreferences.
Is this the right way to do this? If you exit the app, in login screen I'm checking if SharedPreferences contains username/password and try login with those parameteres.. If I want to log out, I clear those parameters from SharedPreferences so next time user opens the app, SharedPreferences won't contain them, so user won't be logged in, he/she would have to type those again...
What are your thoughts about this, is there anything I should do differently?
Regards!
This is so far the easiest method i have ever tried for "Basic Authentication".
Use the below code to generate the auth header (API/Repository class)
var basic = Credentials.basic("YOUR_USERNAME", "YOUR_PASSWORD")
Pass this as header to the webservice call (API/Repository class)
var retrofitCall = myWebservice.getNewsFeed(basic)
Add the basic header as parameter (Retrofit Webservice interface class)
#GET("newsfeed/daily")
fun getNewsFeed(#Header("Authorization") h1:String):Call<NewsFeedResponse>
Sorry, my code is in Kotlin, but can be easily translated to Java.
I'm creating an authorization app, where user registers and gets client_id, client_secret, access_token and refresh_token. I have one API where i need to do call. In that call I use my access_token. All works great. But the access_token expires after hour, so with refresh_token I'm updating my access_token, but the new access_token not works. When I'm doing call with this new access_token, the response body message is "expired access_token provided". In postman all works good. When I'm getting new access_token, in server visible only the access_token that i got from registration. So in server the access_token not updating. But when I'm doing this in postman, the access_token in server changes. So what's the problem, that in postman he updates the access_token, and in server the access_token changes, but when I'm updating in phone, the access_token in server not changes. I done debugging and i see that I'm getting new access_token. So where's the problem?
public interface SupportopApi {
//Post request for user register
#POST("/api/registration")
Call<ResponseBody> registrationRequest(#Body SupportopObjRegistration supportopObjRegistration);
//Post request for user activation
#POST("/api/getClientCD")
Call<ResponseBody> clientActivationRequest(#Body SupportopObjClient activate);
//Get request for getting token
#GET("/api/getToken")
Call<ResponseBody> getTokenRequest(#Query("grant_type") String grant_type,
#Query("client_id") String client_id,
#Query("client_secret") String client_secret,
#Query("email") String email,
#Query("password") String password);
//The call where i use my access_token
#GET("/api/getLanguages")
Call<ResponseBody> getLanguages(#Header("Content-Type") String json,
#Header("Authorization") String token,
#Header("Cache-Control") String cache);}
Here's the retrofit and OkHttpClient initialize part.
public class ApiClient {
private static ApiClient instance;
private SupportopApi supportopApi;
client.addInterceptor(new Interceptor() {
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Request request = chain.request();
request = request.newBuilder()
.build();
return chain.proceed(request);
}
});
supportopApi = new Retrofit.Builder()
.baseUrl(endpoint)
.client(client.build())
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(SupportopApi.class);
}
public static synchronized void initializeInstance(String endpoint) {
if (instance == null) {
instance = new ApiClient(endpoint);
}
}
public static synchronized ApiClient getInstance() {
if (instance == null) {
throw new IllegalStateException("PentairAPIClient has not been initialized.");
}
return instance;
}
public Call<ResponseBody> registration(SupportopObjRegistration supportopObjRegistration) {
return supportopApi.registrationRequest(supportopObjRegistration);
}
public Call<ResponseBody> activation(SupportopObjClient activate) {
return supportopApi.clientActivationRequest(activate);
}
public Call<ResponseBody> getToken(String grant_type, String client_id, String client_secret,
String email, String password) {
return supportopApi.getTokenRequest(grant_type, client_id, client_secret, email, password);
}
public Call<ResponseBody> getLanguage(String token) {
String new_token = "Bearer " + token;
return supportopApi.getLanguages("application/json", new_token, "no-cache");
}
}
Registration works great, so I'll show you only the login call.
public class LoginFragment extends BaseFragment {
private View mainView;
private ApiClient apiClient;
private EditText email, password;
private Button userLogin;
private SupportopObjClient supportopClientActivate;
#Override
public String toString() {
return "LoginFragment";
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
mainView = inflater.inflate(R.layout.login_fragment, container, false);
init(mainView);
return mainView;
}
private void init(final View v) {
apiClient = ApiClient.getInstance();
email = (EditText) v.findViewById(R.id.login_email);
password = (EditText) v.findViewById(R.id.login_password);
userLogin = (Button) v.findViewById(R.id.user_login);
userLogin.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
supportopClientActivate.setUsername("");
supportopClientActivate.setEmail(email.getText().toString());
supportopClientActivate.setPassword(password.getText().toString());
supportopClientActivate.setType("generic");
getClient();
}
});
}
public void getClient() {
Call<ResponseBody> callActive = apiClient.activation(supportopClientActivate);
callActive.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
//Parsing the data from Json to string
String data = response.body().string();
JSONObject obj = new JSONObject(data);
String client_id = obj.getString("client_id");
String client_secret = obj.getString("client_secret");
//Saving clientID and clientSecret in phone storage
SharedPreferencesManager.getInstance().setClientID(client_id);
SharedPreferencesManager.getInstance().setClientSecret(client_secret);
//Calling the tokenCall method to get access token and refresh token
loginCall(client_id, client_secret);
} catch (JSONException | IOException e) {
e.printStackTrace();
}
} else {
//if the response not successful
Toast.makeText(getActivity(), "user doesn't exist", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(getActivity(), "An error occurred", Toast.LENGTH_SHORT).show();
}
});
}
public void loginCall(String client_id, final String client_secret) {
Call<ResponseBody> token = apiClient.getToken("password", client_id, client_secret,
supportopClientActivate.getEmail(), supportopClientActivate.getPassword());
token.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
if (response.isSuccessful()) {
try {
//Parsing the data from Json to string
String dataAccess = response.body().string();
JSONObject obj = new JSONObject(dataAccess);
String access_token = obj.getString("accessToken");
String refresh_token = obj.getString("refreshToken");
Toast.makeText(context, access_token, Toast.LENGTH_SHORT).show();
SharedPreferencesManager.getInstance().setAccessToken(access_token);
SharedPreferencesManager.getInstance().setRefreshToken(refresh_token);
Toast.makeText(context, SharedPreferencesManager.getInstance().getAccessToken(), Toast.LENGTH_SHORT).show();
} catch (IOException | JSONException e) {
e.printStackTrace();
}
} else {
Toast.makeText(getActivity(), "password or email are incorrect or doesn't exist",
Toast.LENGTH_SHORT).show();
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
Toast.makeText(getActivity(), "An error occurred", Toast.LENGTH_SHORT).show();
}
});
}
I want to post user credentials to following url
: http://myurl/authenticate
Parameters : login. Type (JSON)
username : string
password : string
"login":{"username": "JohnDoe","password": "eoDnhoJ" }
If success
{
" r e s u l t " : " S u c c e s s " ,
"response": "Users Session ID"
}
Here is my code
public interface APIService {
#POST("/authenticate")
#FormUrlEncoded
Call<Login> savePost(#Field("username") String username,
#Field("password") String password);
}
public class ApiUtils {
private ApiUtils() {}
public static final String BASE_URL = "http://myurl/";
public static APIService getAPIService() {
return RetrofitClient.getClient(BASE_URL).create(APIService.class);
}
}
public class Login {
#SerializedName("username")
#Expose
private String username;
#SerializedName("password")
#Expose
private String password;
//getters and setters
}
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 LoginActivity extends AppCompatActivity {
private EditText usernameEditText,passwordEditText;
private Button button;
private APIService mAPIService;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_login);
usernameEditText=(EditText)findViewById(R.id.username);
passwordEditText=(EditText)findViewById(R.id.password);
button=(Button)findViewById(R.id.signup);
mAPIService = ApiUtils.getAPIService();
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String uname=usernameEditText.getText().toString();
String pass=passwordEditText.getText().toString();
if(TextUtils.isEmpty(uname)){
Toast.makeText(LoginActivity.this, "Username cannot be empty", Toast.LENGTH_SHORT).show();
return;
}
if(TextUtils.isEmpty(pass)){
Toast.makeText(LoginActivity.this, "Password cannot be empty", Toast.LENGTH_SHORT).show();
return;
}
if(pass.length()<4){
Toast.makeText(LoginActivity.this, "Password should be greater than four characters", Toast.LENGTH_SHORT).show();
return;
}
sendPost(uname, new StringBuilder(uname).reverse().toString());
}
});
}
public void sendPost(String username, String password) {
mAPIService.savePost(username, password).enqueue(new Callback<Login>() {
#Override
public void onResponse(Call<Login> call, Response<Login> response) {
if(response.isSuccessful()) {
showResponse(response.body().toString());
Log.i("Pritish", "post submitted to API." + response.body().toString());
Intent intent=new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
}
#Override
public void onFailure(Call<Login> call, Throwable t) {
Log.e("Pritish", "Unable to submit post to API.");
}
});
}
public void showResponse(String response) {
Log.i("Abbu",response);
}
}
Whenever i submit username and password i get null values,can some body please help me?And how can iget the sessionId.I tried looking for various egs but i am so confsued right now.
Instead of follwing code
#POST("/authenticate")
#FormUrlEncoded
Call<Login> savePost(#Field("username") String username,
#Field("password") String password);
Use this code
#POST("/authenticate")
Call<Login> savePost(#Query("username") String username,
#Query("password") String password);
Step 1: instead of this code
public interface APIService {
#POST("/authenticate")
#FormUrlEncoded
Call<Login> savePost(#Field("username") String username,
#Field("password") String password);
}
Use this code:
public interface APIService {
#POST("/authenticate")
Call<Login> savePost(#Body RequestBody body);
}
Step 2: instead of this code in LoginActivity
public void sendPost(String username, String password) {
mAPIService.savePost(username, password).enqueue(new Callback<Login>() {
#Override
public void onResponse(Call<Login> call, Response<Login> response) {
if(response.isSuccessful()) {
showResponse(response.body().toString());
Log.i("Pritish", "post submitted to API." + response.body().toString());
Intent intent=new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
}
#Override
public void onFailure(Call<Login> call, Throwable t) {
Log.e("Pritish", "Unable to submit post to API.");
}
});
}
Change to this code :
public void sendPost(String username, String password) {
HashMap<String, String> params = new HashMap<>();
params.put("username", username);
params.put("password", password);
String strRequestBody = new Gson().toJson(params);
//create requestbody
final RequestBody requestBody = RequestBody.create(MediaType.
parse("application/json"),strRequestBody);
mAPIService.savePost(requestBody).enqueue(new Callback<Login>() {
#Override
public void onResponse(Call<Login> call, Response<Login> response) {
if(response.isSuccessful()) {
showResponse(response.body().toString());
Log.i("Pritish", "post submitted to API." + response.body().toString());
Intent intent=new Intent(getApplicationContext(), MainActivity.class);
startActivity(intent);
finish();
}
}
#Override
public void onFailure(Call<Login> call, Throwable t) {
Log.e("Pritish", "Unable to submit post to API.");
}
});
}
Replace your Login class by following
#SerializedName("result")
#Expose
private String rESULT;
#SerializedName("response")
#Expose
private String response;
public String getRESULT() {
return rESULT;
}
public void setRESULT(String rESULT) {
this.rESULT = rESULT;
}
public String getResponse() {
return response;
}
public void setResponse(String response) {
this.response = response;
}
Add ServiceGenerator class :
public class ServiceGenerator {
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(BASEURL)
.addConverterFactory(ScalarsConverterFactory.create());
public static <S> S createService(Class<S> serviceClass) {
Retrofit retrofit = builder.client(httpClient.build()).build();
return retrofit.create(serviceClass);
}
public static Retrofit getRetrofit()
{
return builder.client(httpClient.build()).build();
}
}
2.Add interface RetrofitAPI :
public interface RetrofitApi {
#POST("/api/v1/user")
Call<ResponseBody> login(#Body RequestBody loginBody);
}
3.Add method for login in your manager class :
public void retrofitLogin(JSONObject login, final String tag) {
RetrofitApi service = ServiceGenerator.createService(RetrofitApi.class);
Call<ResponseBody> result = service.login(convertJsonToRequestBody(login));
result.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, retrofit2.Response<ResponseBody> response) {
retrofitCheckResponse(response, tag);
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
if (t instanceof IOException) {
Log.e("retrofit error", "retrofit error");
sendErrorRetrofit(mContext.getString(R.string.ERROR), 500, tag);
}
}
});
}
Method to convert JSONObject to RequestBody :
private RequestBody convertJsonToRequestBody(JSONObject jsonObject) {
if (jsonObject != null) {
return RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"), jsonObject.toString());
} else {
return null;
}
}
4.Now call your retrofitLogin method :
JSONObject mLoginParams = new JSONObject();
JSONObject mLoginObj = new JSONObject();
mLoginParams.put("username", uname);
mLoginParams.put("password", pass);
mLoginObj.put("appType","mobile");
mLoginObj.put("user", mLoginParams);
volleyRequest.retrofitLogin(mLoginObj, "Login");
I have a method that makes a call to my server using Retrofit:
public class MainActivity extends AppCompatActivity {
// ... activity methods here, removed for simplicity ...
// Used to subscribe to a user given their userId
public void subscribeToUser(int userId) {
final ApiInterface apiService = ApiClient.createService(ApiInterface.class);
Call<BasicResponse> call = apiService.subscribe(userId);
call.enqueue(new Callback<BasicResponse>() {
#Override
public void onResponse(Call<BasicResponse> call, Response<BasicResponse> response) {
if (response.isSuccessful()) {
Toast.makeText(MainActivity.this, "Success", Toast.LENGTH_LONG).show();
} else {
Toast.makeText(MainActivity.this, "Failed", Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<BasicResponse> call, Throwable t) {
Log.e(TAG, t.toString());
}
});
}
}
I now need to use this same method (subscribeToUser()) in another activity, but it doesn't make sense to copy and paste the method into the other activity. Then I would just have the same code twice.
So can I put the method into one place and have it let the activities know whether or not the call succeeded or failed? How should I organize this?
Here is my ApiClient.java class:
public class ApiClient {
public static final String API_BASE_URL = "http://www.website.com/api/";
private static OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
private static Retrofit.Builder builder =
new Retrofit.Builder()
.baseUrl(API_BASE_URL)
.addConverterFactory(GsonConverterFactory.create());
public static <S> S createService(Class<S> serviceClass) {
Retrofit retrofit = builder.client(httpClient.build()).build();
return retrofit.create(serviceClass);
}
public static <S> S createService(Class<S> serviceClass, final String authToken) {
if (authToken != null) {
httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Interceptor.Chain chain) throws IOException {
Request original = chain.request();
// Request customization: add request headers
Request.Builder requestBuilder = original.newBuilder()
.header("Authorization", "Bearer " + authToken)
.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);
}
}
And here is my ApiInterface.java class:
public interface ApiInterface {
#FormUrlEncoded
#POST("subscribe")
Call<BasicResponse> subscribe(#Field("userId") Integer userId);
}
Thanks.
In my opinion, createService(ApiInterface.class) shouldn't be invoked multiple times. It's not necessary and slows down your application. You can try to create UserService with singleton pattern as below:
public class UserService {
private UserService userService;
final ApiInterface apiService;
//Contructor private to prevent init object from outside directly.
private UserService() {
apiService = ApiClient.createService(ApiInterface.class);
}
//use this method when you need to use UserService
public static UserService getInstance() {
if(userService == null) {
userService = new UserService();
}
}
// Used to subscribe to a user given their userId
public void subscribeToUser(int userId, ServiceCallBack serviceCallBack) {
final ApiInterface apiService = ApiClient.createService(ApiInterface.class);
Call<BasicResponse> call = apiService.subscribe(userId);
call.enqueue(new Callback<BasicResponse>() {
#Override
public void onResponse(Call<BasicResponse> call, Response<BasicResponse> response) {
if (response.isSuccessful()) {
Toast.makeText(MainActivity.this, "Success", Toast.LENGTH_LONG).show();
serviceCallBack.successful(response);
} else {
Toast.makeText(MainActivity.this, "Failed", Toast.LENGTH_LONG).show();
}
}
#Override
public void onFailure(Call<BasicResponse> call, Throwable t) {
Log.e(TAG, t.toString());
serviceCallBack.fail(t);
}
});
}
//this is callback interface, help you know whether success from outside.
interface ServiceCallBack {
void successful(Response response);
void fail(Throwable t);
}
}
How to use:
UserService.getInstance(1, new ServiceCallBack(){
#Override
public void successful(Response response) {
//process successful
}
#Override
public void fail(Throwable t) {
//process fail
}
});
Now you can put all methods relate to User api to UserService class to reuse.