java - 使用改造向请求添加 header 时如何避免在主线程上等待?

标签 java android multithreading retrofit

我用它来配置我的改造:

 RestAdapter restAdapter = new RestAdapter.Builder()

            //add headers to requests
            .setRequestInterceptor(getAuthenticatedRequestInterceptor())
            .setEndpoint(BASE_URL)
            .setConverter(new GsonConverter(getGson()))
            .build();

并且 getAuthenticatedRequestInterceptor() 方法将 header 添加到请求中:

public AccountRequestInterceptor getAuthenticatedRequestInterceptor() {
    AccountRequestInterceptor interceptor = new AccountRequestInterceptor();
    Map<String, String> headers = new HashMap<>();
     String accessToken = null;
    try {
        accessToken = TokenProvider.getInstance(mContext).getToken();
    } catch (InterruptedException e) {

    }
    headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
    interceptor.setHeader(headers);
    return interceptor;
}

getToken() 方法是:

private synchronized string getToken() throws InterruptedException {
    if (!isRefreshing()) {
        //This is very important to call notify() on the same object that we call wait();
        final TokenProvider myInstance = this;
        setRefreshing(true);

        MyApplication.getRestClient().getAccountService().getRefreshedToken(mLoginData.getRefreshToken())
                .subscribe(new Observer<LoginResponse>() {
                    @Override
                    public void onCompleted() {
                        synchronized (myInstance) {
                            setRefreshing(false);
                            myInstance.notifyAll();
                        }
                    }

                    @Override
                    public void onError(Throwable e) {
                        synchronized (myInstance) {
                            setRefreshing(false);
                            myInstance.notifyAll();
                        }
                    }

                    @Override
                    public void onNext(LoginResponse loginResponse) {
                        synchronized (myInstance) {
                            mLoginData = loginResponse;
                            mAccountProvider.saveLoginData(loginResponse);
                            myInstance.notifyAll();
                        }
                    }
                });
    }
    this.wait();
    return mLoginData.getToken();
}

TokenProvider.getInstance(mContext).getToken() 在主线程上有一个 wait() 来获取异步方法的响应,我知道这是一个不好的事情,但我需要在这里等待响应从中获取 token 然后返回 token 。我如何在单独的线程中执行此操作以避免在主线程上等待?

注意:

1 - 在任何带有改造的请求之前调用它。

2 - 我 read this我知道我可以在请求失败后刷新 token ,但出于商业原因,我想避免使用无效 token 。

3 - 我在我的 Activity 中调用 MyApplication.getRestClient().getAccountService().login(loginRequest,callback...‌ ) 并在添加 token 之前调用所有内容发生在后台线程中。所以我想使用我的 token 而不阻塞主线程。

更新:我将以下拦截器添加到我的新 OkHttp 中:

public class RequestTokenInterceptor implements Interceptor {
    @Override
    public Response intercept(Interceptor.Chain chain) throws IOException {
        Request request = chain.request();
        Request newRequest;
        try {
            Log.d("addHeader", "Before");
            String token = TokenProvider.getInstance(mContext).getToken();
            if (token != null) {
                newRequest = request.newBuilder()
                        .addHeader("Bearer", token)
                        .build();
            } else {
                // I want to cancel the request or raise an exception to catch it in onError method
                // of retrofit callback.
            }
        } catch (InterruptedException e) {
            Log.d("addHeader", "Error");
            e.printStackTrace();
            return chain.proceed(request);
        }
        Log.d("addHeader", "after");
        return chain.proceed(newRequest);
    }
}

现在,如果 token 为空,我如何取消请求或引发异常以在改进回调的 onError 方法中捕获它?

最佳答案

这是一个有点奇怪的问题,但让我试着帮助你。 :)

如您所知,您可以在请求失败后使用响应拦截器进行改造来刷新 token 。

让我们尝试在请求之前使用拦截器。

public class RequestTokenInterceptor implements Interceptor {
   @Override
   public Response intercept(Chain chain) throws IOException {
      Request request = chain.request();
      // Here where we'll try to refresh token.
      // with an retrofit call
      // After we succeed we'll proceed our request
      Response response = chain.proceed(request);
      return response;
   }
}

当你创建你的 api 时,创建一个新的 HttpClient:

OkHttpClient client = new OkHttpClient();
client.interceptors().add(new RequestTokenInterceptor());

然后将您的 http 客户端添加到您的适配器,如下所示:

.setClient(new OkClient(client))

如果这可行,在每次请求之前,您将首先尝试刷新 token ,然后继续您的 api 请求。所以在 ui 中与您的普通 api 调用没有区别。

编辑:

我也在编辑我的答案。如果您想在其他情况下返回错误,如果 token 为空,则您可以创建自定义响应:

private Response(Builder builder) {
    this.request = builder.request;
    this.protocol = builder.protocol;
    this.code = builder.code;
    this.message = builder.message;
    this.handshake = builder.handshake;
    this.headers = builder.headers.build();
    this.body = builder.body;
    this.networkResponse = builder.networkResponse;
    this.cacheResponse = builder.cacheResponse;
    this.priorResponse = builder.priorResponse;
  }

或者您可以简单地返回一个空响应。如果您构建自定义响应并将代码设置为不为 200(例如 401 或 400+),您将在 Retrofit 的回调失败方法中收到该响应。比你可以做任何你想做的。

如果您返回 null,我认为您将得到一个 RuntimeException,并且您仍然可以在回调的失败方法中捕获响应。

在 else 中创建您自己的响应后,您可以创建自定义回调并捕获您的空响应并按照您想要的方式转换您的自定义错误,如下所示:

public abstract class DefaultRequestCallback<T> implements Callback<T> {

    public abstract void failure(YourCustomException ex);

    public abstract void success(T responseBean);

    @Override
    public void success(T baseResponseBean, Response response) {
        if (response == null) {
            // Here we catch null response and transform it to our custom     Exception
            failure(new YourCustomException());
        }
        } else {
            success(baseResponseBean);
        }
    }

    @Override
    public void failure(RetrofitError error) {
        // Here's your failure method.
        // Also you can transform default retrofit errors to your customerrors
        YourCustomException ex = new YourCustomException();
        failure(ex);
    }
}

我想这对你有帮助。

编辑 2:

您可以像下面这样构建一个新的 Response。 Retrofit 的 Response 类中有一个构建器模式。您可以从那里查看。

Response response = new Response.Builder().setCode(401).setMessage("Error Message").build();

关于java - 使用改造向请求添加 header 时如何避免在主线程上等待?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32175717/

相关文章:

java - 如何像FunctionalInterface一样实现注解来限制成员函数的数量、函数类型等?

java - 将数据从 Activity 传递到 SurfaceView

java - 如何从 JavaFX 中的 TextArea 获取文本并保存换行符?

c# - 处理线程中 catch block 内抛出的异常的最佳实践。 (。网)

java - 如何在运行时调试 jar?

android - 带 Action 的倒数计时器循环

java - 如何修复 "array type expected found java.util.arraylist"?

android - 如何使用 AndroidHttpClient(API 级别 8)和 UsernamePasswordCredentials?

java - 看起来Servlet中只有一个线程处理所有请求

python - 检测并发中失败的任务。futures