Java 如何避免在使用改型向请求添加头时等待主线程?

Java 如何避免在使用改型向请求添加头时等待主线程?,java,android,multithreading,retrofit,Java,Android,Multithreading,Retrofit,我使用此配置我的改装: RestAdapter restAdapter = new RestAdapter.Builder() //add headers to requests .setRequestInterceptor(getAuthenticatedRequestInterceptor()) .setEndpoint(BASE_URL) .setConverter(new GsonConv

我使用此配置我的改装:

 RestAdapter restAdapter = new RestAdapter.Builder()

            //add headers to requests
            .setRequestInterceptor(getAuthenticatedRequestInterceptor())
            .setEndpoint(BASE_URL)
            .setConverter(new GsonConverter(getGson()))
            .build();
并且
getAuthenticatedRequestInterceptor()
方法将头添加到请求中:

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;
}
TokenProvider.getInstance(mContext).getToken()有一个
wait()
在主线程上获取异步方法的响应,我知道这样做不好,但我需要在这里等待响应从中获取令牌,然后返回令牌。如何在单独的线程中执行此操作以避免在主线程上等待

注:

1-在任何改装请求之前调用此命令

2-我和我知道我可以在请求失败后刷新令牌,但出于业务原因,我希望避免使用无效令牌

3-我调用
MyApplication.getRestClient().getAccountService().login(loginRequest,回调。。。‌​)
在我的
活动中
添加令牌之前,所有事情都发生在后台线程中。因此我希望使用我的令牌,不要阻止主线程

更新:我将以下
拦截器添加到我的新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);
    }
}

现在,如果令牌为空,我如何取消请求或引发异常以在改型回调的一个错误方法中捕获它?

您可以在AsyncTask doInBackground方法中执行所有长操作,而在onPre和onPostExecute中,您可以在用户等待时显示/隐藏一些进度条

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

正如您所知,您可以在请求失败后使用响应拦截器进行更新来刷新令牌

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

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))
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);
    }
}
如果这样做有效,在每次请求之前,您将首先尝试刷新令牌,然后继续您的api请求。因此,在ui中,它与您的普通api调用没有区别

编辑:

我也在编辑我的答案。如果要在else情况下返回错误(如果令牌为null),则在else情况下,您可以创建自定义响应:

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+,您将在改造的回调失败方法中收到该响应。你想做什么就做什么

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

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

.setClient(new OkClient(client))
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);
    }
}

好的,我认为如果您在主线程上调用getAuthenticatedRequestInterceptor(),然后又调用getInstance(),我觉得您将创建一个TokenProvider类型的对象,因此当您在主线程中创建此对象时,您的对象。wait()在主线程上运行,因此要在后台线程上运行,可能需要修改getAuthenticatedRequestInterceptor()方法以在新线程中执行以下行

try {
    accessToken = TokenProvider.getInstance(mContext).getToken();
} catch (InterruptedException e) {

}
headers.put(HeadersContract.HEADER_AUTHONRIZATION, O_AUTH_AUTHENTICATION + accessToken);
interceptor.setHeader(headers);
return interceptor;
但是由于主线程将继续执行,因此在通知您的RestAdapter时会有问题,因此我建议
调用getAuthenticatedRequestInterceptor()方法,然后通知您的主线程来构建您的重新适配器。这将释放您的主线程,但使用您正在使用的策略,您必须等到收到令牌后才能进行任何调用。

是否在主线程上发送请求?我使用MyApplication.getRestClient().getAccountService().login(登录请求、回调…)在我的活动中,在添加令牌之前,所有事情都发生在后台线程中。@immibis所说的,也要避免像瘟疫一样的AsyncTask。取而代之的是,例如,使用IntentServices。下面是AsyncTask不好的一个例子:@dazito是对的,这使得所有事情都变得更加困难。这是应用程序拦截器还是网络拦截器?它是a一个应用程序拦截器。好的,看起来它正在工作,但为什么我的拦截器在主线程中运行,而这个拦截器在改型定义的线程中?我需要更多的东西:如果令牌为null,我需要取消请求或引发自定义异常,然后再将其添加到intercept方法中的请求。我怎么做?我不确定,但在我的e我试图解释的示例。在您呼叫chain之前。继续(请求)你可以进行检查,如果为空,则不继续请求。只需使用应用程序拦截器,你就可以在网络请求之前或之后向网络请求添加一个代码块。你可以做任何你想做的事情。我不想这样做,因为我将不得不改变我调用我应用程序中迄今为止发出的每个请求的方式。这是sh你只需要改变你的RestAdapter的创建。然而更重要的是,我试图解释为什么你的对象在主线程上等待。