使用改造向请求添加 headers 时如何避免在主线程上等待?
How to avoid waiting on main thread when adding headers to requests using retrofit?
我用它来配置我的改造:
RestAdapter restAdapter = new RestAdapter.Builder()
//add headers to requests
.setRequestInterceptor(getAuthenticatedRequestInterceptor())
.setEndpoint(BASE_URL)
.setConverter(new GsonConverter(getGson()))
.build();
和getAuthenticatedRequestInterceptor()
方法将headers添加到请求中:
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()
以从异步方法获取响应,我知道这是一件坏事,但我需要在这里等待响应从中获取令牌,然后 return token.how 我可以在单独的线程中执行此操作以避免在主线程上等待吗?
注:
1 - 在任何带有改造的请求之前调用它。
2 - 我 read this 我知道我可以在请求失败后刷新令牌,但出于商业原因,我想避免使用无效令牌。
3 - 我在我的 Activity
中调用 MyApplication.getRestClient().getAccountService().login(loginRequest,callback...)
,在添加令牌之前,一切都发生在后台线程中。所以我想使用我的令牌而不阻塞主线程。
更新: 我将以下 Interceptor
添加到我的新 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);
}
}
现在,如果令牌为空,我如何取消请求或引发异常以在改进回调的 onError 方法中捕获它?
您可以在 AsyncTask doInBackground 方法中执行所有长动作,而在 onPre- 和 onPostExecute 中您可以 show/hide 在用户等待时显示一些进度条
这个问题有点奇怪,但让我试着帮助你。 :)
如您所知,您可以在请求失败后使用响应拦截器进行改造来刷新令牌。
让我们尝试在请求之前使用拦截器。
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))
如果这有效,在每次请求之前,您将首先尝试刷新令牌,然后继续您的 api 请求。所以在 ui 中与您正常的 api 调用没有区别。
编辑:
我也在编辑我的答案。如果您想 return 在其他情况下如果令牌为空则出错,在其他情况下您可以创建自定义响应:
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;
}
或者您可以 return 一个空响应。如果你 build 你的自定义响应并将你的代码设置为不为 200,例如 401 或 400+,你将在 Retrofit 的回调失败方法中收到该响应。比你可以做任何你想做的。
如果你 return 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:
您可以uild 如下所示的新回复。 Retrofit 的响应 class 中有一个 builder 模式。您可以从那里查看。
Response response = new Response.Builder().setCode(401).setMessage("Error Message").build();
好的,我想如果你在主线程上调用你的 getAuthenticatedRequestInterceptor() 并且它又调用 getInstance(),我觉得你会在其中创建一个 TokenProvider 类型的对象,因此当你在主线程你的 object.wait() 运行s 在主线程上因此到 运行 this 在后台线程上可能修改你的 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() 方法,然后通知您的主线程构建您的 RestAdapter.This 将释放您的主线程,但是根据您采用的策略,您必须等到收到令牌才能进行任何操作来电。
我用它来配置我的改造:
RestAdapter restAdapter = new RestAdapter.Builder()
//add headers to requests
.setRequestInterceptor(getAuthenticatedRequestInterceptor())
.setEndpoint(BASE_URL)
.setConverter(new GsonConverter(getGson()))
.build();
和getAuthenticatedRequestInterceptor()
方法将headers添加到请求中:
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()
以从异步方法获取响应,我知道这是一件坏事,但我需要在这里等待响应从中获取令牌,然后 return token.how 我可以在单独的线程中执行此操作以避免在主线程上等待吗?
注:
1 - 在任何带有改造的请求之前调用它。
2 - 我 read this 我知道我可以在请求失败后刷新令牌,但出于商业原因,我想避免使用无效令牌。
3 - 我在我的 Activity
中调用 MyApplication.getRestClient().getAccountService().login(loginRequest,callback...)
,在添加令牌之前,一切都发生在后台线程中。所以我想使用我的令牌而不阻塞主线程。
更新: 我将以下 Interceptor
添加到我的新 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);
}
}
现在,如果令牌为空,我如何取消请求或引发异常以在改进回调的 onError 方法中捕获它?
您可以在 AsyncTask doInBackground 方法中执行所有长动作,而在 onPre- 和 onPostExecute 中您可以 show/hide 在用户等待时显示一些进度条
这个问题有点奇怪,但让我试着帮助你。 :)
如您所知,您可以在请求失败后使用响应拦截器进行改造来刷新令牌。
让我们尝试在请求之前使用拦截器。
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))
如果这有效,在每次请求之前,您将首先尝试刷新令牌,然后继续您的 api 请求。所以在 ui 中与您正常的 api 调用没有区别。
编辑:
我也在编辑我的答案。如果您想 return 在其他情况下如果令牌为空则出错,在其他情况下您可以创建自定义响应:
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;
}
或者您可以 return 一个空响应。如果你 build 你的自定义响应并将你的代码设置为不为 200,例如 401 或 400+,你将在 Retrofit 的回调失败方法中收到该响应。比你可以做任何你想做的。
如果你 return 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:
您可以uild 如下所示的新回复。 Retrofit 的响应 class 中有一个 builder 模式。您可以从那里查看。
Response response = new Response.Builder().setCode(401).setMessage("Error Message").build();
好的,我想如果你在主线程上调用你的 getAuthenticatedRequestInterceptor() 并且它又调用 getInstance(),我觉得你会在其中创建一个 TokenProvider 类型的对象,因此当你在主线程你的 object.wait() 运行s 在主线程上因此到 运行 this 在后台线程上可能修改你的 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() 方法,然后通知您的主线程构建您的 RestAdapter.This 将释放您的主线程,但是根据您采用的策略,您必须等到收到令牌才能进行任何操作来电。