/
ProxyHandler.java
172 lines (158 loc) · 6.87 KB
/
ProxyHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*
* Copyright (C) 2016 david.wei (lighters)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.lighters.demos.token.http.proxy;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;
import com.lighters.demos.app.base.BaseApplication;
import com.lighters.demos.token.http.GlobalToken;
import com.lighters.demos.token.http.IGlobalManager;
import com.lighters.demos.token.http.RetrofitUtil;
import com.lighters.demos.token.http.api.IApiService;
import com.lighters.demos.token.http.api.TokenModel;
import com.lighters.demos.token.http.exception.TokenInvalidException;
import com.lighters.demos.token.http.exception.TokenNotExistException;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Date;
import retrofit2.http.Query;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Func1;
/**
* Created by david on 16/8/21.
* Email: huangdiv5@gmail.com
* GitHub: https://github.com/alighters
*/
public class ProxyHandler implements InvocationHandler {
private final static String TAG = "Token_Proxy";
private final static String TOKEN = "token";
private final static int REFRESH_TOKEN_VALID_TIME = 30;
private static long tokenChangedTime = 0;
private Throwable mRefreshTokenError = null;
private boolean mIsTokenNeedRefresh;
private Object mProxyObject;
private IGlobalManager mGlobalManager;
public ProxyHandler(Object proxyObject, IGlobalManager globalManager) {
mProxyObject = proxyObject;
mGlobalManager = globalManager;
}
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Throwable {
return Observable.just(null).flatMap(new Func1<Object, Observable<?>>() {
@Override
public Observable<?> call(Object o) {
try {
try {
if (mIsTokenNeedRefresh) {
updateMethodToken(method, args);
}
return (Observable<?>) method.invoke(mProxyObject, args);
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}).retryWhen(new Func1<Observable<? extends Throwable>, Observable<?>>() {
@Override
public Observable<?> call(Observable<? extends Throwable> observable) {
return observable.flatMap(new Func1<Throwable, Observable<?>>() {
@Override
public Observable<?> call(Throwable throwable) {
if (throwable instanceof TokenInvalidException) {
return refreshTokenWhenTokenInvalid();
} else if (throwable instanceof TokenNotExistException) {
// Token 不存在,执行退出登录的操作。(为了防止多个请求,都出现 Token 不存在的问题,
// 这里需要取消当前所有的网络请求)
mGlobalManager.exitLogin();
return Observable.error(throwable);
}
return Observable.error(throwable);
}
});
}
});
}
/**
* Refresh the token when the current token is invalid.
*
* @return Observable
*/
private Observable<?> refreshTokenWhenTokenInvalid() {
synchronized (ProxyHandler.class) {
// Have refreshed the token successfully in the valid time.
if (new Date().getTime() - tokenChangedTime < REFRESH_TOKEN_VALID_TIME) {
mIsTokenNeedRefresh = true;
return Observable.just(true);
} else {
// call the refresh token api.
RetrofitUtil.getInstance().get(IApiService.class).refreshToken().subscribe(new Subscriber<TokenModel>() {
@Override
public void onCompleted() {
}
@Override
public void onError(Throwable e) {
mRefreshTokenError = e;
}
@Override
public void onNext(TokenModel model) {
if (model != null) {
mIsTokenNeedRefresh = true;
tokenChangedTime = new Date().getTime();
GlobalToken.updateToken(model.token);
Log.d(TAG, "Refresh token success, time = " + tokenChangedTime);
}
}
});
if (mRefreshTokenError != null) {
return Observable.error(mRefreshTokenError);
} else {
return Observable.just(true);
}
}
}
}
/**
* Update the token of the args in the method.
*
* PS: 因为这里使用的是 GET 请求,所以这里就需要对 Query 的参数名称为 token 的方法。
* 若是 POST 请求,或者使用 Body ,自行替换。因为 参数数组已经知道,进行遍历找到相应的值,进行替换即可(更新为新的 token 值)。
*/
private void updateMethodToken(Method method, Object[] args) {
if (mIsTokenNeedRefresh && !TextUtils.isEmpty(GlobalToken.getToken())) {
Annotation[][] annotationsArray = method.getParameterAnnotations();
Annotation[] annotations;
if (annotationsArray != null && annotationsArray.length > 0) {
for (int i = 0; i < annotationsArray.length; i++) {
annotations = annotationsArray[i];
for (Annotation annotation : annotations) {
if (annotation instanceof Query) {
if (TOKEN.equals(((Query) annotation).value())) {
args[i] = GlobalToken.getToken();
}
}
}
}
}
mIsTokenNeedRefresh = false;
}
}
}