Какая лучшая практика для повторного запроса - PullRequest
0 голосов
/ 16 сентября 2018

Мой AsyncHttpClient класс выглядит следующим образом.

ApiHttpClient.java

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);
    }
}

В моей деятельности

MainActivity.java

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));
            }
        });
    }
}

Когда токен истекает, API возвращает JSONObject , содержащий {"error": "token_expired"} и ошибку 401 .

В методе logoutUser () я обрабатываю ошибки с помощью switch. В случае сбоя запроса ( onFailure ) из-за истечения срока действия токена и возврата ошибки 401 , как лучше всего отправить POST запрос на обновление токен, а затем повторно отправить запрос на выход, например.

...