title: What Best Practice to Submit a Failed Request Again
body: <p>My <code>AsyncHttpClient</code> class is as follows.</p>
  
  <p><em>ApiHttpClient.java</em></p>
  
  <pre><code>package virtualsystems.com.br.financial.client;
  
  import android.content.Context;
  import android.util.Log;
  
  import com.loopj.android.http.AsyncHttpClient;
  import com.loopj.android.http.AsyncHttpResponseHandler;
  import com.loopj.android.http.RequestParams;
  
  import cz.msebera.android.httpclient.Header;
  import cz.msebera.android.httpclient.HttpRequestInterceptor;
  import cz.msebera.android.httpclient.HttpResponseInterceptor;
  import cz.msebera.android.httpclient.impl.client.DefaultHttpClient;
  
  public class ApiHttpClient {
      private static final String BASE_URL = "http://MY_IP_LOCAL_NETWORK/";
      private static final Integer RETRY_COUNT = 2;
      private static final Integer RETRY_DELAY = 500;
      private static final Boolean URL_ENCODING = true;
  
      private static AsyncHttpClient client = new AsyncHttpClient();
  
      public ApiHttpClient() {
          client.addHeader("Content-Type", "application/json");
          client.addHeader("Accept", "application/json");
          client.setUserAgent("Apache-HttpClient-AsyncHttpClient (Linux; Android 6.0) UNAVAILABLE (Java/0) Mobile");
          client.setURLEncodingEnabled(URL_ENCODING);
          client.setMaxRetriesAndTimeout(RETRY_COUNT, RETRY_DELAY);
      }
  
      private static String getAbsoluteUrl(String relativeUrl) {
          return BASE_URL + relativeUrl;
      }
  
      public static void get(Context context, String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
          client.get(context, getAbsoluteUrl(url), params, responseHandler);
      }
  
      public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
          client.post(getAbsoluteUrl(url), params, responseHandler);
      }
  
      public static void delete(Context context, String url, Header[] headers, RequestParams params, AsyncHttpResponseHandler responseHandler) {
          client.delete(context, getAbsoluteUrl(url), headers, params, responseHandler);
      }
  
      public static void put(Context context, String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
          client.put(context, getAbsoluteUrl(url), params, responseHandler);
      }
  
      public static void addResponseInterceptor(HttpResponseInterceptor interceptor) {
          Log.d("ApiHttpClient", client.getHttpClient().getClass().getName());
          Log.d("ApiHttpClient", "is DefaultHttpClient: " + (client.getHttpClient() instanceof DefaultHttpClient));
          DefaultHttpClient defaultHttpClient = (DefaultHttpClient) client.getHttpClient();
          defaultHttpClient.addResponseInterceptor(interceptor);
      }
  
      public static void addRequestInterceptor(HttpRequestInterceptor interceptor) {
          Log.d("ApiHttpClient", client.getHttpClient().getClass().getName());
          Log.d("ApiHttpClient", "is DefaultHttpClient: " + (client.getHttpClient() instanceof DefaultHttpClient));
          DefaultHttpClient defaultHttpClient = (DefaultHttpClient) client.getHttpClient();
          defaultHttpClient.addRequestInterceptor(interceptor);
      }
  }
  </code></pre>
  
  <p>In my Activity</p>
  
  <p><em>MainActivity.java</em></p>
  
  <pre><code>package virtualsystems.com.br.financial;
  
  import android.annotation.SuppressLint;
  import android.content.Intent;
  import android.os.Bundle;
  import android.os.StrictMode;
  import android.support.v4.app.Fragment;
  import android.support.v4.app.FragmentTransaction;
  import android.support.design.widget.NavigationView;
  import android.support.v4.view.GravityCompat;
  import android.support.v4.widget.DrawerLayout;
  import android.support.v7.app.ActionBarDrawerToggle;
  import android.support.v7.app.AppCompatActivity;
  import android.support.v7.widget.Toolbar;
  import android.view.Menu;
  import android.view.MenuItem;
  import android.view.View;
  import android.widget.ImageView;
  import android.widget.TextView;
  import android.widget.Toast;
  
  import com.bumptech.glide.Glide;
  import com.bumptech.glide.request.RequestOptions;
  import com.loopj.android.http.AsyncHttpResponseHandler;
  
  import java.io.IOException;
  
  import cz.msebera.android.httpclient.Header;
  import cz.msebera.android.httpclient.HttpException;
  import cz.msebera.android.httpclient.HttpRequest;
  import cz.msebera.android.httpclient.HttpRequestInterceptor;
  import cz.msebera.android.httpclient.client.HttpClient;
  import cz.msebera.android.httpclient.impl.client.HttpClientBuilder;
  import cz.msebera.android.httpclient.protocol.HttpContext;
  import virtualsystems.com.br.financial.client.ApiHttpClient;
  
  public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
      ApiHttpClient apiHttpClient = new ApiHttpClient();
      UserSession userSession = UserSession.getInstance(MainActivity.this);
  
      @SuppressLint("CheckResult")
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
  
          StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
          StrictMode.setThreadPolicy(policy);
  
          Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);UserSession userSession = UserSession.getInstance(getApplicationContext());
          setSupportActionBar(toolbar);
  
          DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
          ActionBarDrawerToggle toggle = new ActionBarDrawerToggle( this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
          drawer.addDrawerListener(toggle);
          toggle.syncState();
  
          NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view);
          navigationView.setNavigationItemSelectedListener(this);
  
          View headerView = navigationView.getHeaderView(0);
          TextView navUsername = (TextView) headerView.findViewById(R.id.nav_header_username);
          TextView navEmail = (TextView) headerView.findViewById(R.id.nav_header_email);
          ImageView navAvatar = (ImageView) headerView.findViewById(R.id.nav_header_avatar);
  
          navUsername.setText(userSession.getUserName());
          navEmail.setText(userSession.getUserEmail());
  
          String imgURL = (String) userSession.getUserAvatar();
  
          RequestOptions requestOptions = new RequestOptions();
          requestOptions.placeholder(R.mipmap.ic_launcher_round);
          requestOptions.error(R.mipmap.ic_launcher_round);
  
          Glide.with(this)
                  .load(imgURL)
                  .apply(requestOptions)
                  .into(navAvatar);
  
          displayFragment(R.id.nav_home);
      }
  
      @Override
      public void onBackPressed() {
          DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
          if (drawer.isDrawerOpen(GravityCompat.START)) {
              drawer.closeDrawer(GravityCompat.START);
          } else {
              super.onBackPressed();
          }
      }
  
      @Override
      public boolean onCreateOptionsMenu(Menu menu) {
          // Inflate the menu; this adds items to the action bar if it is present.
          getMenuInflater().inflate(R.menu.main, menu);
          return true;
      }
  
      @Override
      public boolean onOptionsItemSelected(MenuItem item) {
          // Handle action bar item clicks here. The action bar will
          // automatically handle clicks on the Home/Up button, so long
          // as you specify a parent activity in AndroidManifest.xml.
          int id = item.getItemId();
  
          //noinspection SimplifiableIfStatement
          if (id == R.id.action_settings) {
              Toast.makeText(this, "Settings Clicked", Toast.LENGTH_SHORT).show();
              return true;
          }
  
          return super.onOptionsItemSelected(item);
      }
  
      @SuppressWarnings("StatementWithEmptyBody")
      @Override
      public boolean onNavigationItemSelected(MenuItem item) {
          // Handle navigation view item clicks here.
          int id = item.getItemId();
          displayFragment(id);
  
          return true;
      }
  
      private void displayFragment(int itemId){
          Fragment fragment = null;
  
          switch (itemId){
              case R.id.nav_categories:
                  fragment = new Categories();
                  break;
              case R.id.nav_home:
                  fragment = new BillsTotal();
                  break;
              case R.id.nav_bills:
                  fragment = new Bills();
                  break;
              case R.id.nav_logout:
                  logoutUser();
                  break;
          }
  
          if (fragment != null){
              FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
              ft.replace(R.id.content_frame, fragment);
              ft.commit();
          }
  
          DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
          drawer.closeDrawer(GravityCompat.START);
      }
  
      private void logoutUser() {
          apiHttpClient.addRequestInterceptor(new HttpRequestInterceptor() {
              @Override
              public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
                  request.addHeader("Authorization", "Bearer " + userSession.getUserToken());
              }
          });
  
          apiHttpClient.post("api/logout", null, new AsyncHttpResponseHandler(){
              String responseMessage = "";
  
              @Override
              public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {
                  responseMessage = "Disconected";
              }
  
              @Override
              public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {
                  switch (statusCode){
                      case 400:
                          responseMessage = "Bad Request - Invalid Token";
                          break;
                      case 401:
                          responseMessage = "Unauthorized - Invalid Token";
                          break;
                      case 500:
                          responseMessage = "Could not Invalidate Token";
                          break;
                      default:
                          String errorCode = ((Integer) statusCode).toString();
                          String errorMsg = responseBody.toString();
                          responseMessage = "Error: " + errorCode + "\n" + errorMsg;
                          break;
                  }
              }
  
              @Override
              public void onFinish() {
                  super.onFinish();
                  userSession.clearSession();
                  Toast.makeText(getApplicationContext(), responseMessage, Toast.LENGTH_SHORT).show();
                  startActivity(new Intent(getApplicationContext(), LoginActivity.class));
              }
          });
      }
  }
  </code></pre>
  
  <p>When the token expires, the API returns a <strong>JSONObject</strong> containing <code>{"error": "token_expired"}</code> and error <strong>401</strong>.</p>
  
  <p>In the <em>logoutUser()</em> method, I handle errors with switch. In case the request fails (<strong>onFailure</strong>) because the token is expired, and error <strong>401</strong> is returned, what is the best way to send a <strong>POST</strong> request to update the token and then resend the logout request, for example.</p>