最近项目中遇到一个需求,token过期的自动处理。我们项目中网络请求模块使用了rxJava+Retrofit的组合,相信看到这个文章的人都对此比较了解了,不多赘述。
看到token过期处理首先想到的是在请求回调中获取到token过期的信息,然后进行刷新操作,但是由于项目中使用到的网络请求接口众多,而且大多数接口都有可能会出现token过期的情况,如此处理就显得比较麻烦了。
百度之,发现了使用代理来处理的方法,见大神博客:http://alighters.com/blog/2016/08/22/rxjava-plus-retrofitshi-xian-zhi-demo/?utm_source=tuicool&utm_medium=referral ,本篇文章主要讲一讲我在实践中遇到的问题以及解决的方案。
token过期处理主要用到如下4个类
首先提及我们项目中统一Response的model封装:
public class MResponse implements Serializable {
public String responseCode;//结果码
public T responseData;
public String responseToken;
}
当正确返回数据时,responseCode为success,当出现token过期时 responseCode为token_timeout.
alighters在其demo对 GsonConverterFactory类中的responseBodyConverter方法中的Type做了包装,其包装代码如下:
public Converter responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
Type newType = new ParameterizedType() {
@Override
public Type[] getActualTypeArguments() {
return new Type[] { type };
}
@Override
public Type getOwnerType() {
return null;
}
@Override
public Type getRawType() {
return ApiModel.class;
}
};
TypeAdapter> adapter = gson.getAdapter(TypeToken.get(newType));
return new GsonResponseBodyConverter<>(adapter);
}
public Converter responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
TypeAdapter> adapter = gson.getAdapter(TypeToken.get(type));
return new GsonResponseBodyConverter<>(gson, adapter);
}
这样也避免了Type的类型锁死为我们的Model封装。
接下来就到了真正抛出token_timeout异常的地方了,在GsonResponseBodyConverter中,直接看代码:
public Object convert(ResponseBody value) throws IOException {
try {
JsonReader jsonReader = gson.newJsonReader(value.charStream());
Object obj = adapter.read(jsonReader);
if (obj instanceof MResponse) {//token过期则抛出异常
if (((MResponse) obj).responseCode.equals("token_timeout")) {
throw new IllegalArgumentException("token_timeout");//异常类型可以自己定义
}
}
return obj;
} finally {
value.close();
}
}
使用instanceof使得响应的Model封装不再固定为MResponse,如果有其他类型可以加上else if ,扩展性更好一些。
添加代理的方式请参照alighters的文章,这里不再描述,贴上一段ProxyHandler中invoke()方法的代码:
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
final InvokeArguments invokeArguments = new InvokeArguments();//变量集合类,避免使用ProxyHandler的成员变量。
return Observable.just(null).flatMap(new Func1
refreshTokenWhenTokenInvalid代码:
private Observable> refreshTokenWhenTokenInvalid(final InvokeArguments invokeArguments) {
synchronized (ProxyHandler.class) {
invokeArguments.refreshTokenError = null;
// call the refresh token api.
ApiWrapper.getInstance().refreshToken()
.subscribe(new Subsciber() {
@Override
public void onNext(MResponse model) {
if (model != null) {
invokeArguments.isTokenNeedRefresh = true;
tokenChangedTime = new Date().getTime();
GlobalToken.updateToken(model.responseToken);//此处使用自己的token存放方式
System.out.println("Refresh token success, time = " + tokenChangedTime);
}
}
@Override
public void onError(Throwable e) {
invokeArguments.refreshTokenError = e;
}
});
if (invokeArguments.refreshTokenError != null) {
return Observable.error(invokeArguments.refreshTokenError);
} else {
return Observable.just(true);
}
}
}
updateMethodToken方法:
private void updateMethodToken(InvokeArguments invokeArguments, Object[] args) {
if (invokeArguments.isTokenNeedRefresh && !TextUtils.isEmpty(your new token)) {
for (int i = 0, len = args.length; i < len; i++) {
if (args[i] instanceof RequestParams) {
((RequestParams) args[i]).requestToken = your new token;
}
}
invokeArguments.isTokenNeedRefresh = false;
}
}
后记:做完token刷新,本地测试ok之后 兴冲冲的使用服务端的数据来测试,结果居然失败了,罪魁祸首是什么?请看下集,gson解析同一位置不同类型json数据。