In my app I have a spinner that includes some language abbreviation example (en, az, and others), I want to set selected language abbreviation as the request header and I need this in every request so I am saving abbreviation in shared preferences and get abbreviation in my ApiClient class,
every time that spinner selection has changed I change shared preferences abbreviation value but header only set the first time I select a language and when I change spinner selection header not change
This is my ApiClient class
private static final String BASE_URL = ApiUrls.server_url;
public static Retrofit getClient(Context context)
{
SharedPrefUtil sharedPrefUtil = new SharedPrefUtil(context);
String locale = sharedPrefUtil.getSelectedLanguage();
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient = httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException
{
Request request = chain.request().newBuilder().header("Accept-Language",
locale).build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).client(httpClient.build())
.build();
return retrofit;
}
This is my ApiRequester class method for sending a request to server
public static void sendLogin(final Context context, Map<String, String> params,
final HttpResponses.onLoginSuccess onLoginSuccess) {
DialogHelper.ProgressDialog progressDialog = showWaitDialog(context);
if (hasInternetConnection(context)) {
params.put("grant_type", "password");
params.put("client_id", "raymon-client");
params.put("client_secret", "raymon-secret");
ApiInterface apiService = ApiClient.getClient(context).create(ApiInterface.class);
Call<ResponseBody> call = apiService.loginRequest(params);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
progressDialog.dismiss();
//result ok
if (response.code() == 200)
{
try
{
onLoginSuccess.loginResponse(JsonSuccessParser.parseLoginResponse
(context, response.body().string()));
} catch (IOException e)
{
e.printStackTrace();
}
}
else if (response.code() == 403)
{
onLoginSuccess.loginError(response.code());
}
else
{
try
{
JsonErrorParser.parseServerError(context,response.errorBody().string());
} catch (IOException e)
{
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
progressDialog.dismiss();
showNoConnectionDialog(context);
}
});
} else {
progressDialog.dismiss();
showNoConnectionDialog(context);
}
}
This is my code for setting language abbreviation in shared preferences
private void setUserLocale()
{
String selected_country = countryCodeAdapter.getItem(country_code_spinner.
getSelectedItemPosition()).language.abbreviation.toLowerCase();
LocaleHelper.setLocale(this,selected_country);
}
How can I change header immediately when user change spinner position?
In ApiClient class, you should initialize locale variable inside of interceptor methods to reflect your changes.
Example)
private static final String BASE_URL = ApiUrls.server_url;
public static Retrofit getClient(Context context)
{
OkHttpClient.Builder httpClient = new OkHttpClient.Builder();
httpClient = httpClient.addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException
{
SharedPrefUtil sharedPrefUtil = new SharedPrefUtil(context);
String locale = sharedPrefUtil.getSelectedLanguage();
Request request = chain.request().newBuilder().header("Accept-Language",
locale).build();
return chain.proceed(request);
}
});
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).client(httpClient.build())
.build();
return retrofit;
}
However, if the value changes constantly, it may be possible to explicitly pass the value of the Header as an argument to the Service instead of the Interceptor.
Example)
public interface ApiService {
Call<...> loginRequest(#Header("Accept-Language") String language, #QueryMap Map<String, String> params);
}
public static void sendLogin(final Context context, Map<String, String> params,
final HttpResponses.onLoginSuccess onLoginSuccess) {
DialogHelper.ProgressDialog progressDialog = showWaitDialog(context);
if (hasInternetConnection(context)) {
params.put("grant_type", "password");
params.put("client_id", "raymon-client");
params.put("client_secret", "raymon-secret");
SharedPrefUtil sharedPrefUtil = new SharedPrefUtil(context);
String locale = sharedPrefUtil.getSelectedLanguage();
ApiInterface apiService = ApiClient.getClient(context).create(ApiInterface.class);
Call<ResponseBody> call = apiService.loginRequest(locale, params);
call.enqueue(new Callback<ResponseBody>() {
#Override
public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
progressDialog.dismiss();
//result ok
if (response.code() == 200)
{
try
{
onLoginSuccess.loginResponse(JsonSuccessParser.parseLoginResponse
(context, response.body().string()));
} catch (IOException e)
{
e.printStackTrace();
}
}
else if (response.code() == 403)
{
onLoginSuccess.loginError(response.code());
}
else
{
try
{
JsonErrorParser.parseServerError(context,response.errorBody().string());
} catch (IOException e)
{
e.printStackTrace();
}
}
}
#Override
public void onFailure(Call<ResponseBody> call, Throwable t) {
progressDialog.dismiss();
showNoConnectionDialog(context);
}
});
} else {
progressDialog.dismiss();
showNoConnectionDialog(context);
}
}
Related
textView = findViewById(R.id.textVieww);
String url = "https://zenquotes.io/api/random";
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(url)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(Call call, IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(Call call, Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = response.body().string();
try {
JSONArray jsonarray = new JSONArray(myResponse);
for(int i=0; i<jsonarray.length(); i++) {
JSONObject obj = jsonarray.getJSONObject(i);
}
} catch (JSONException e) {
e.printStackTrace();
}
Quote.this.runOnUiThread(() ->
textView.setText(myResponse));
}
}
});
}
This is the part im stuck on i think im on the right track but not sure where to go from here im trying to get the "q" information from the returned url and the "a" information but it just outputs everything any suggestions?
What was your problem
Even when you parsed JSON string, you were still using the myResponse string in your textView.setText() method.
Continuing your code snippet
your code snippet is quite short, but i do think i can quite understand what you mean.
So let's say that we have Activity, which is called MainActivity and in that activity we have two views, one TextView called that has an id of tv_author_and_quote and one Button which has a xml id btn_request_quote.
The button has an OnClickListener which calls method requestForQuote().
Our onCreate + the variables of Button and TextView looks like this:
TextView tvAuthorAndQuote;
Button btnRequestQuote;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvAuthorAndQuote = findViewById(R.id.tv_author_and_quote);
btnRequestQuote = findViewById(R.id.btn_request_quote);
btnRequestQuote.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
requestForQuote();
}
});
}
And then we have a code itself for method requestForQuote():
public void requestForQuote() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = Objects.requireNonNull(response.body()).string();
String myFormattedQuote = "";
try {
JSONArray jsonarray = new JSONArray(myResponse);
for(int i=0; i<jsonarray.length(); i++) {
JSONObject obj = jsonarray.getJSONObject(i);
String quote = obj.getString("q");
String author = obj.getString("a");
Log.d(TAG, "onResponse: quote:" + quote);
Log.d(TAG, "onResponse: author:" + author);
myFormattedQuote = author + ": " + quote;
}
} catch (JSONException e) {
e.printStackTrace();
}
final String myFinalQuote = myFormattedQuote;
MainActivity.this.runOnUiThread(() -> {
if (!myFinalQuote.equals("")) {
tvAuthorAndQuote.setText(myFinalQuote);
} else {
tvAuthorAndQuote.setText(myResponse);
}
});
}
}
});
}
The code above basically uses your existing solution, but instead of setting the text of textView with myResponse string, it parses the json array and gets a quote and an author from it. Then it just logs it (just for testing purposes), then it constructs the string which gets displayed to the if there is any, otherwise it prints the response. That it is.
Using Gson library
import it into your gradle dependecies
implementation 'com.google.code.gson:gson:2.8.7'
Write short "holder" class called Quote
public class Quote {
public Quote() {
}
String q;
String a;
String h;
public String getQ() {
return q;
}
public void setQ(String q) {
this.q = q;
}
public String getA() {
return a;
}
public void setA(String a) {
this.a = a;
}
public String getH() {
return h;
}
public void setH(String h) {
this.h = h;
}
#NonNull
#NotNull
#Override
public String toString() {
return a + ": " + q;
}
}
Then the requestForQuote() method could look something like this:
public void requestForQuoteWithGson() {
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder()
.url(URL)
.build();
client.newCall(request).enqueue(new Callback() {
#Override
public void onFailure(#NotNull Call call, #NotNull IOException e) {
e.printStackTrace();
}
#Override
public void onResponse(#NotNull Call call, #NotNull Response response) throws IOException {
if (response.isSuccessful()) {
final String myResponse = Objects.requireNonNull(response.body()).string();
Type listType = new TypeToken<ArrayList<Quote>>(){}.getType();
List<Quote> yourClassList = new Gson().fromJson(myResponse, listType);
if (yourClassList != null && yourClassList.size() > 0) {
final Quote quote = yourClassList.get(0);
if (quote != null) {
myQuotes.add(quote);
MainActivity.this.runOnUiThread(() ->
tvAuthorAndQuote.setText(quote.toString())
);
}
}
}
}
});
}
A method build in Java using Jersey which takes two parameters and store in database it works properly with the postman but when I use it in Android Application it not work. I try to send a request using Volley and Retrofit.
Server Side Code:
#POST
#Produces(MediaType.APPLICATION_JSON)
#Path("/register")
public Boolean registerUser(#FormParam("userName") String userName, #FormParam("password") String password) {
System.out.println(userName+"\t"+password);
String insertQuery = "INSERT INTO user(user_name,password,status) VALUES(?,?,?)";
try {
Connection con = MyConnection.getConnection();
PreparedStatement prst = con.prepareStatement(insertQuery);
prst.setString(1, userName);
prst.setString(2, password);
prst.setInt(3, 0);
int count = prst.executeUpdate();
con.close();
System.out.println(count+" Row inserted");
return true;
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return false;
}
Android Code :
public void register(final String userName, final String password) {
User user = new User(userName, password, 1);
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://192.168.1.13:8080/Demo_Application/")
.addConverterFactory(GsonConverterFactory.create())
.build();
JsonPlaceholderApi jsonPlaceholderApi = retrofit.create(JsonPlaceholderApi.class);
Call<List<User>> call = jsonPlaceholderApi.register("application/x-www-form-urlencoded", user);
call.enqueue(new Callback<List<User>>() {
#Override
public void onResponse(Call<List<User>> call, Response<List<User>> response) {
if (!response.isSuccessful()){
Log.e("Response","Something went wrong."+response.toString());
return;
}
Log.d("Response",response.toString());
}
#Override
public void onFailure(Call<List<User>> call, Throwable t) {
Log.e("Response",t.getMessage());
}
});
}
Postman Response
Volley Request:
public void registerVolley(final String userName, final String password){
Map<String, String> param = new HashMap<>();
param.put("userName", userName);
param.put("password",password);
JsonObjectRequest arrayRequest = new JsonObjectRequest(Request.Method.POST, "http://192.168.0.26:8080/Demo_Application/rs/test/register", new JSONObject(param), new com.android.volley.Response.Listener<JSONObject>() {
#Override
public void onResponse(JSONObject response) {
Log.e("Response", response.toString());
}
}, new com.android.volley.Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Log.e("Response", error.toString());
}
}){
#Override
protected Map<String, String> getParams() throws AuthFailureError {
Map<String, String> param = new HashMap<>();
param.put("userName", userName);
param.put("password",password);
return param;
}
#Override
public Map<String, String> getHeaders() throws AuthFailureError {
Map<String, String> header = new HashMap<>();
header.put("Content-Type","application/json");
return header;
}
};
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(arrayRequest);
}
Your code for retrofit:
JsonPlaceholderApi jsonPlaceholderApi = retrofit.create(JsonPlaceholderApi.class);
Call<Boolean> call = jsonPlaceholderApi.sign("userName", "password");
call.enqueue(new Callback<Boolean>() {
#Override
public void onResponse(Call<Boolean> call, Response<Boolean> response) {
if (!response.isSuccessful()){
Log.e("Response","Something went wrong."+response.toString());
return;
}
Log.d("Response",response.toString());
}
#Override
public void onFailure(Call<Boolean> call, Throwable t) {
Log.e("Response",t.getMessage());
}
});
Your method inside jsonPlaceholderApi :
#FormUrlEncoded
#POST("rs/test/register")
Call<ResponseLogin> signIn(
#Field("userName") String userName,
#Field("password") String password
);
Add Below Code in proguard-rules.pro
-keepattributes *Annotation*
-keepclassmembers class ** {
#org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }
-keep class com.app.appname.model.** { *; }
NOTE: Change last line with your model folder
How to start LoginActivity from the interceptor(non-activity class)? I have tried the code (Interceptor) below but not working for me.
Interceptor
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + auth_token_string)
.build();
Response response = chain.proceed(newRequest);
Log.d("MyApp", "Code : "+response.code());
if (response.code() == 401){
Intent intent = new Intent(SplashActivity.getContextOfApplication(), LoginActivity.class);
startActivity(intent);
finish(); //Not working
return response;
}
return chain.proceed(newRequest);
}
}).build();
This is the current solution I'm using, is there any better solution than this? This solution has to keep repeat on every api call.
MainActivity
call.enqueue(new Callback<Token>() {
#Override
public void onResponse(Call<Token> call, Response<Token> response) {
if(response.isSuccessful())
{
//success
}
else
{
Intent intent = new Intent(MainActivity.this.getApplicationContext(), LoginActivity.class);
startActivity(intent);
finish();
}
}
#Override
public void onFailure(Call<Token> call, Throwable t) {
}
});
Personally, I would like to suggest using event bus pattern here. You can use greenrobot implementation or whatever you want, since it's more about an architecture approach rather than concrete implementation.
Create event model
public class UnauthorizedEvent {
private static final UnauthorizedEvent INSTANCE = new UnauthorizedEvent();
public static UnauthorizedEvent instance() {
return INSTANCE;
}
private UnauthorizedEvent() {
}
}
Implement custom Interceptor which disptaches event about unauthorized reqeusts
class UnauthorizedInterceptor implements Interceptor {
#Override
public Response intercept(#NonNull Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if (response.code() == 401) {
EventBus.getDefault().post(UnauthorizedEvent.instance());
}
return response;
}
}
Create BaseActivity class which handles UnauthorizedEvent
public class BaseActivity extends Activity {
#Override
public void onStart() {
super.onStart();
EventBus.getDefault().register(this);
}
#Override
public void onStop() {
super.onStop();
EventBus.getDefault().unregister(this);
}
#Subscribe
public final void onUnauthorizedEvent(UnauthorizedEvent e) {
handleUnauthorizedEvent();
}
protected void handleUnauthorizedEvent() {
Intent intent = new Intent(this, LoginActivity.class);
startActivity(intent);
}
}
Prevent launching LoginActivity from LoginActivity
public class LoginActivty extends BaseActivity {
#Override
protected void handleUnauthorizedEvent() {
//Don't handle unauthorized event
}
}
Another approach is to not extending BaseActivity here.
Register your interceptor
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new UnauthorizedInterceptor())
.build();
Pros:
Loose coupling between components
Easaly extending the logic by overriding handleUnauthorizedEvent
No need to rewrite code to use new types of callbacks
Reduce human factor about making mistakes (using Callback instead of BaseCallback)
Cons:
EventBus pattern makes debugging more complicated
One more dependency or own implementation which brings new code to the project
Also, please take into account, that this example doesn't cover multithreading issues. It solves your problem of handling unauthorized requests. Thus, if two requests receive 401 than it is possible that 2 instances of LoginActivity is started.
Consider introducing a custom implementation of retrofit2.Callback interface, e.g. BaseCallback:
public abstract class BaseCallback<T> implements Callback<T> {
private final Context context;
public BaseCallback(Context context) {
this.context = context;
}
#Override
public void onResponse(Call<T> call, Response<T> response) {
if (response.code() == 401) {
// launch login activity using `this.context`
} else {
onSuccess(response.body());
}
}
#Override
public void onFailure(Call<T> call, Throwable t) {
}
abstract void onSuccess(T response);
}
Now, from the caller site you should change new Callback<Token> with new BaseCallback<Token>:
call.enqueue(new BaseCallback<Token>(context) {
#Override
void onSuccess(Token response) {
// do something with token
}
});
Although, this approach doesn't fulfil your following statement:
so I don't have to keep repeat the same code over again for each api call
nevertheless, I cannot come up with a better approach.
The simplest way is to inject activity context in Interceptor instance.
If you are using some DI tools, like Dagger2 or Toothpick it will be very simple. I recommend to use toothpick)
https://github.com/stephanenicolas/toothpick
The most code near by will be in kotlin, because it's my boilerplate code. Those thinks, that you are need to solve your problem i will write in Java.
The solution will be like this:
#Qualifier
annotation class BackendUrl
class ActivityModule(activity: BaseActivity): Module() {
init {
bind(Activity::class.java).toInstance(activity)
}
}
class NetworkModule: Module() {
init {
bind(String::class.java).withName(BackendUrl::class.java).toInstance(Constants.URL)
bind(Gson::class.java).toInstance(GsonBuilder().setDateFormat("yyyy-MM-dd'T'HH:mm:ss").create())
bind(CacheHolder::class.java).toProvider(CacheProvider::class.java).singletonInScope()
bind(OkHttpClient::class.java).toProvider(OkHttpProvider::class.java).instancesInScope()
bind(BackendApi::class.java).toProvider(BackendApiProvider::class.java).instancesInScope()
bind(RedirectInterceptor::class.java).to(RedirectInterceptor::class.java)
}
}
Than you need to create Providers for injection dependency
class BackendApiProvider #Inject constructor(
private val okHttpClient: OkHttpClient,
private val gson: Gson,
#BackendUrl private val serverPath: String
) : Provider<BackendApi> {
override fun get() =
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.client(okHttpClient)
.baseUrl(serverPath)
.build()
.create(BackendApi::class.java)
}
And your redirect interceptor:
public class RedirectInterceptor implements Interceptor {
private final Context context;
#Inject
public RedirectInterceptor(Activity context) {
this.context = context;
}
#Override
public Response intercept(Chain chain) throws IOException {
Request newRequest = chain.request().newBuilder()
.build();
Response response = chain.proceed(newRequest);
Log.d("MyApp", "Code : "+response.code());
if (response.code() == 401){
Intent intent = new Intent(context, LoginActivity.class);
context.startActivity(intent);
((Activity) context).finish();
return response;
}
return chain.proceed(newRequest);
}
}
Oh, yes. For Authorization header will be better to create new instance of another interceptor
class HeaderInterceptor(private val token: String?) : Interceptor {
#Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val newRequest = request.newBuilder()
Log.d(TAG, "token: $token")
if (token != null && token.isNotBlank()) {
newRequest.addHeader("Authorization", "Bearer $token")
}
return chain.proceed(newRequest.build())
}
companion object {
private val TAG = HeaderInterceptor::class.java.toString()
}
}
And your OkhttpProvder
class OkHttpProvider #Inject constructor(cacheHolder: CacheHolder, prefs: IPreferences, redirectInterceptor: RedirectInterceptor) : Provider<OkHttpClient> {
private val client: OkHttpClient
init {
val builder = OkHttpClient.Builder()
builder
.addNetworkInterceptor(redirectInterceptor)
.addNetworkInterceptor(HeaderInterceptor(prefs.getAuthToken()))
.readTimeout(30, TimeUnit.SECONDS)
.cache(cacheHolder.okHttpCache)
client = builder.build()
}
override fun get() = client
}
Thats all! Now, you just only need to place you modules in right scopes.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.init_view)
Toothpick.openScopes("activity scope").apply {
installModules(ActivityModule(this#YourActivity))
Toothpick.inject(this#YourActivity, this)
}
Toothpick.openScopes("activity scope", "network scope").apply {
installModules(NetworkModule())
}
// your activity code
}
This is how interceptor worked for handling 401 globally
public class ResponseHeaderInterceptor implements Interceptor {
private final Context context;
public ResponseHeaderInterceptor(Context context) {
this.context = context;
}
#NotNull
#Override
public Response intercept(#NotNull Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
if(response.code() == 401){
SharedPreferences pref = context.getSharedPreferences(Constants.PREFERENCES, 0);
String userName = pref.getString("key_user_email", "");
//clear shared preferences
pref.edit().clear().apply();
Bundle params = new Bundle();
params.putString("user", userName);
FirebaseAnalytics.getInstance(context).logEvent(Constants.USER_UNAUTHORIZED_EVENT, params);
Intent intent = new Intent(this.context, IntroActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
this.context.startActivity(intent);
}
return response;
}
}
adding to okhttp client of retrofit
var okHttpClient: OkHttpClient = OkHttpClient()
.newBuilder()
.addInterceptor(ResponseHeaderInterceptor(MyApplication.getMyApplicationContext()))//Header interceptor for logging network responses
.build()
private var retrofit: Retrofit? = null
val client: Retrofit?
get() {
if (retrofit == null) {
retrofit = Retrofit.Builder()
.client(okHttpClient)
.baseUrl(BuildConfig.SERVER)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
return retrofit
}
Generalized Solution:
You can solve it by generalizing the error handling. You can use custom CallAdapterFactory to the Retrofit builder. Please refer below classes :
RxErrorHandlingCallAdapterFactory :
public class RxErrorHandlingCallAdapterFactory extends CallAdapter.Factory {
private static Context mContext = null;
private final RxJava2CallAdapterFactory original;
private RxErrorHandlingCallAdapterFactory() {
original = RxJava2CallAdapterFactory.create();
}
public static CallAdapter.Factory create(Context context) {
mContext = context;
return new RxErrorHandlingCallAdapterFactory();
}
#Override
public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
return new RxCallAdapterWrapper(retrofit, original.get(returnType, annotations, retrofit));
}
private static class RxCallAdapterWrapper<R> implements CallAdapter<R, Object> {
private final Retrofit retrofit;
private final CallAdapter<R,
Object> wrapped;
public RxCallAdapterWrapper(Retrofit retrofit, CallAdapter<R, Object> wrapped) {
this.retrofit = retrofit;
this.wrapped = wrapped;
}
#Override
public Type responseType() {
return wrapped.responseType();
}
#Override
public Object adapt(Call<R> call) {
Object result = wrapped.adapt(call);
if (result instanceof Single) {
return ((Single) result).onErrorResumeNext(new Function<Throwable, SingleSource>() {
#Override
public SingleSource apply(#NonNull Throwable throwable) throws Exception {
return Single.error(asRetrofitException(throwable));
}
});
}
if (result instanceof Observable) {
return ((Observable) result).onErrorResumeNext(new Function<Throwable, ObservableSource>() {
#Override
public ObservableSource apply(#NonNull Throwable throwable) throws Exception {
return Observable.error(asRetrofitException(throwable));
}
});
}
if (result instanceof Completable) {
return ((Completable) result).onErrorResumeNext(new Function<Throwable, CompletableSource>() {
#Override
public CompletableSource apply(#NonNull Throwable throwable) throws Exception {
return Completable.error(asRetrofitException(throwable));
}
});
}
return result;
}
private RetrofitException asRetrofitException(Throwable throwable) {
// We had non-200 http error
Log.v("log", "eror");
throwable.printStackTrace();
if (throwable instanceof HttpException) {
HttpException httpException = (HttpException) throwable;
final Response response = httpException.response();
//if ((mContext instanceof Activity)) {
String s = "Something went wrong."; //mContext.getString(R.string.something_went_wrong);
try {
s = new JSONObject(response.errorBody().string()).getString("message");
if (response.code() == 401) { // 401 Unauthorized
Intent intent = new Intent(mContext, LoginActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
mContext.startActivity(intent);
}
} catch (JSONException | IOException e) {
e.printStackTrace();
}
return RetrofitException.unexpectedError(s, response, retrofit);
//showErrorDialog(mContext, response);
//}
// return RetrofitException.httpError(response.errorBody().toString(), response, retrofit);
}
// A network error happened
if (throwable instanceof IOException) {
return RetrofitException.networkError((IOException) throwable);
}
// We don't know what happened. We need to simply convert to an unknown error
return RetrofitException.unexpectedError(throwable);
}
}
}
RetrofitException :
public class RetrofitException extends RuntimeException {
private final String url;
private final Response response;
private final Kind kind;
private final Retrofit retrofit;
RetrofitException(String message, String url, Response response, Kind kind, Throwable exception, Retrofit retrofit) {
super(message, exception);
this.url = url;
this.response = response;
this.kind = kind;
this.retrofit = retrofit;
}
public static RetrofitException httpError(String url, Response response, Retrofit retrofit) {
String message = response.code() + " " + response.message();
return new RetrofitException(message, url, response, Kind.HTTP, null, retrofit);
}
public static RetrofitException networkError(IOException exception) {
return new RetrofitException(exception.getMessage(), null, null, Kind.NETWORK, exception, null);
}
public static RetrofitException unexpectedError(Throwable exception) {
return new RetrofitException(exception.getMessage(), null, null, Kind.UNEXPECTED, exception, null);
}
public static RetrofitException unexpectedError(String s, Response response, Retrofit retrofit) {
return new RetrofitException(s, null, null, Kind.UNEXPECTED, null, null);
}
/**
* The request URL which produced the error.
*/
public String getUrl() {
return url;
}
/**
* Response object containing status code, headers, body, etc.
*/
public Response getResponse() {
return response;
}
/**
* The event kind which triggered this error.
*/
public Kind getKind() {
return kind;
}
/**
* The Retrofit this request was executed on
*/
public Retrofit getRetrofit() {
return retrofit;
}
/**
* HTTP response body converted to specified {#code type}. {#code null} if there is no
* response.
*
* #throws IOException if unable to convert the body to the specified {#code type}.
*/
public <T> T getErrorBodyAs(Class<T> type) throws IOException {
if (response == null || response.errorBody() == null) {
return null;
}
Converter<ResponseBody, T> converter = retrofit.responseBodyConverter(type, new Annotation[0]);
return converter.convert(response.errorBody());
}
/**
* Identifies the event kind which triggered a {#link RetrofitException}.
*/
public enum Kind {
/**
* An {#link IOException} occurred while communicating to the server.
*/
NETWORK,
/**
* A non-200 HTTP status code was received from the server.
*/
HTTP,
/**
* An internal error occurred while attempting to execute a request. It is best practice to
* re-throw this exception so your application crashes.
*/
UNEXPECTED
}
}
Retrofit Builder :
Retrofit retrofit = new Retrofit.Builder()
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create(context))
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(API_URL)
.client(client)
.build();
You can handle 401 in RxErrorHandlingCallAdapterFactory and other errors through Throwable.
I have a retrofit 2 response by which i gets data from server. my code is working fine.
But i want cache this response using SharedPreferences and keep it till that activity is running and after activity is destroyed. i want to delete this response from SharedPreferences.
This is my Code:
public class SampleClass {
private DataInterface mListener;
public SampleClass() {
super();
}
public void getDataForId(final String id) {
ApiInterface apiInterface = APIClient.getApiInterface();
Call<MyResponse> call = apiInterface.getResponse();
call.enqueue(new Callback<MyResponse>() {
#Override
public void onResponse(Call<MyResponse> call, Response<MyResponse> response) {
if (response!=null && response.body() != null && mListener != null) {
mListener.responseData(response.body());
}
}
#Override
public void onFailure(Call<MyResponse> call, Throwable t) {
}
});
}
public void setOnDataListener(DataInterface listener) {
mListener = listener;
}
public interface DataInterface {
void responseData( MyResponse myResponse );
}
}
SecondData class file
sampleClass.setOnDataListener(new SampleClass.DataInterface() {
#Override
public void responseData(MyResponse myResponse) {
// i wanna store this response into SharedPreferences for temp and delete after activity is destroyed.
List<Detail> details = myResponse.getDetails();
for (Detail d : details) {
if (d.getId().equals(id)) {
reqDetail = d;
name.setText(reqDetail.getName());
Picasso.with(SecondData.this)
.load(reqDetail.getName())
.placeholder(R.mipmap.ic_launcher)
.error(R.mipmap.ic_launcher)
.into(image);
}
}
}
});
ApiInterface
public interface ApiInterfaceNew {
#GET("/display.php")
Call<MyResponse> getResponse();//imp to include MyResponse as a call
}
Api class
private static final String ROOT_URL = "";
private static Retrofit retrofit1 = null;
private static final String CACHE_CONTROL = "Cache-Control";
public static Retrofit getClient() {
if (retrofit1 == null) {
retrofit1 = new Retrofit.Builder()
.baseUrl(ROOT_URL)
.client(provideOkHttpClient())
.addConverterFactory(GsonConverterFactory.create())
.build();
}
return retrofit1;
}
public static ApiInterfaceNew getApiInterface() {
return getClient().create(ApiInterfaceNew.class);
}
private static OkHttpClient provideOkHttpClient() {
return new OkHttpClient.Builder()
.addInterceptor(provideHttpLoggingInterceptor())
.addInterceptor(provideOfflineCacheInterceptor())
.addNetworkInterceptor(provideCacheInterceptor())
.cache(provideCache())
.build();
}
private static Cache provideCache() {
Cache cache = null;
try {
cache = new Cache(new File(AppControler.getInstance().getCacheDir(), "http-cache"),
10 * 1024 * 1024); // 10 MB
} catch (Exception e) {
Timber.e("Could not create Cache!");
}
return cache;
}
private static HttpLoggingInterceptor provideHttpLoggingInterceptor() {
HttpLoggingInterceptor httpLoggingInterceptor =
new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
#Override
public void log(String message) {
Timber.e(message);
}
});
httpLoggingInterceptor.setLevel(BuildConfig.DEBUG ? HEADERS : NONE);
return httpLoggingInterceptor;
}
public static Interceptor provideCacheInterceptor() {
return new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
// re-write response header to force use of cache
CacheControl cacheControl = new CacheControl.Builder()
.maxAge(2, TimeUnit.MINUTES)
.build();
return response.newBuilder()
.header(CACHE_CONTROL, cacheControl.toString())
.build();
}
};
}
public static Interceptor provideOfflineCacheInterceptor() {
return new Interceptor() {
#Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
if (!AppControler.hasNetwork()) {
CacheControl cacheControl = new CacheControl.Builder()
.maxStale(7, TimeUnit.DAYS)
.build();
request = request.newBuilder()
.cacheControl(cacheControl)
.build();
}
return chain.proceed(request);
}
};
}
AppControler class
public class AppControler extends Application {
private static AppControler instance;
#Override
public void onCreate()
{
super.onCreate();
instance = this;
if (BuildConfig.DEBUG)
{
Timber.plant(new Timber.DebugTree());
}
Timber.i("Creating our Application");
}
public static AppControler getInstance ()
{
return instance;
}
public static boolean hasNetwork ()
{
return instance.checkIfHasNetwork();
}
public boolean checkIfHasNetwork()
{
ConnectivityManager cm = (ConnectivityManager) getSystemService( Context.CONNECTIVITY_SERVICE);
NetworkInfo networkInfo = cm.getActiveNetworkInfo();
return networkInfo != null && networkInfo.isConnected();
}
Add below code in your Retrofit response when a response is success.
First convert JSON to string and store.
Gson gson = new Gson();
String favData = gson.toJson(response.body());
save strings to prefrance.
preferenceManager is my SharedPref class
preferenceManager.setStringPreference(Global.OFFLINE_WORD, favData);
now when you want to get pref data call below method.
public ArrayList<MyResponse> getData(String key) {
String data = getStringPreference(key);
Gson gson = new Gson();
Type type = new TypeToken<ArrayList<MyResponse>>() {
}.getType();
return gson.fromJson(data, type);
}
You can use either ObjectMapper or Gson.
For ObjectMapper you can refer the below code
public static void updateUserInfo(UserInfo userInfo, Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
Crashlytics.log("updating User Info "+(userInfo!=null?userInfo.toString():"UserInfo is null"));
final SharedPreferences.Editor edit = preferences.edit();
ObjectMapper objectMapper = new ObjectMapper();
try {
String value = objectMapper.writeValueAsString(userInfo);
edit.putString("USER_INFO_MODEL", value);
edit.commit();
} catch (JsonProcessingException e) {
Exceptions.propagate(e);
}
}
you can also get the stored response from shared preferences
public static UserInfo getUserInfo(Context context) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String userDetatails = preferences.getString(AppConstants.USER_INFO_MODEL, "");
Crashlytics.log("get UserInfo "+userDetatails);
ObjectMapper mapper = new ObjectMapper();
if (StringUtils.isEmpty(userDetatails)) {
return null;
}
UserInfo userInfo = null;
try {
userInfo = mapper.readValue(userDetatails, UserInfo.class);
} catch (IOException e) {
Exceptions.propagate(e);
}
return userInfo;
}
I want to send a POST RAW data of {"userid": "userid","pass":"1222"} as a user name and password. I have one layout consisting of username and password that will fetch as userid and password. I need help in trying this to retrofit
// Triggers when LOGIN Button clicked
public void checkLogin(View arg0) {
// Initialize AsyncLogin() class with userid and password
new REST().execute();
}
public class REST extends AsyncTask<Void, Void, Void> {
#Override
protected void onPreExecute() {
super.onPreExecute();
// The Username & Password
final EditText usr = (EditText) findViewById(R.id.username);
userid = (String) usr.getText().toString();
final EditText pw = (EditText) findViewById(R.id.password);
password = (String) pw.getText().toString();
}
#Override
protected Void doInBackground(Void... params) {
HttpURLConnection urlConnection=null;
String json = null;
// -----------------------
try {
HttpResponse response;
JSONObject jsonObject = new JSONObject();
jsonObject.accumulate("username", usr);
jsonObject.accumulate("password", password);
json = jsonObject.toString();
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost("http://mark.journeytech.com.ph/mobile_api/authentication.php");
httpPost.setEntity(new StringEntity(json, "UTF-8"));
httpPost.setHeader("Content-Type", "application/json");
httpPost.setHeader("Accept-Encoding", "application/json");
httpPost.setHeader("Accept-Language", "en-US");
response = httpClient.execute(httpPost);
String sresponse = response.getEntity().toString();
Log.w("QueingSystem", sresponse);
Log.w("QueingSystem", EntityUtils.toString(response.getEntity()));
}
catch (Exception e) {
Log.d("InputStream", e.getLocalizedMessage());
} finally {
// nothing to do here
}
return null;
}
#Override
protected void onPostExecute(Void result) {
Toast.makeText(getApplicationContext(), email + " "+ password, Toast.LENGTH_SHORT).show();
if (result != null) {
// do something
} else {
// error occured
}
}
please any help because i searched a lot and didn't reach anything
You need to follow following steps :
Network API interface
public interface NetworkAPI {
#GET("authentication.php")
#Headers({"Content-Type:application/json; charset=UTF-8"})
Call<JsonElement> loginRequest(#Body LoginRequest body);
}
Login Request model class
public class LoginRequest {
String userid;
String password;
public LoginRequest(String userid, String password) {
this. userid = userid;
this. pass = pass;
}
}
Retrofit call in your activity
String baseUrl ="http://mark.journeytech.com.ph/mobile_api/";
NetworkAPI networkAPI;
public void loginRequest(){
Retrofit retrofit = new Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build();
networkAPI = retrofit.create(NetworkAPI.class);
LoginRequest loginRequest = new LoginRequest(yourusername,yourpassword);
Call<JsonElement> call = networkAPI.loginRequest(loginRequest);
call.enqueue(new Callback<JsonElement>() {
#Override
public void onResponse(Call<JsonElement> call, Response<JsonElement> response) {
// success response
}
#Override
public void onFailure(Call<JsonElement> call, Throwable t) {
// failure response
}
});
}
First: you should create your api interface
public interface Api
{
#Headers({"Accept: application/json"})
#FormUrlEncoded
#POST("authentication.php")
Call<Void> Login(#Field("[email]") String email,
#Field("[password]") String password);
}
Second: In your activity you should call your function
void Login(String email, final String password)
{
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://mark.journeytech.com.ph/mobile_api/")
.addConverterFactory(GsonConverterFactory.create())
.build();
Apiservice = retrofit.create(Api.class);
Call<Void> call = service.Login(email, password);
call.enqueue(new Callback<Void>()
{
#Override
public void onResponse(Call<Void> call, Response<Void> response)
{
if (response.isSuccess())
{
}
else
{
}
}
#Override
public void onFailure(Call<Void> call, Throwable t)
{
}
});
}