49手游账号token过期已过期

Retrofit Token过期自动刷新并重新请求接口
在有心课堂的群里,有网友提出如下场景:
当前开发的 App 遇到一个问题:
当请求某个接口时,由于 token 已经失效,所以接口会报错。
但是产品经理希望 app 能够马上刷新 token ,然后重复请求刚才那个接口,这个过程对用户来说是无感的。
请求 A 接口-》服务器返回 token 过期-》请求 token 刷新接口-》请求 A 接口
我们应该是怎么解决这个问题呢?
经过百度搜索到了相关信息,这里总结下。
本文是采用Rx + Retrofit来实现网络请求封装。
利用 Observale 的 retryWhen 的方法,识别 token 过期失效的错误信息,此时发出刷新 token 请求的代码块,完成之后更新 token,这时之前的请求会重新执行,但将它的 token 更新为最新的。另外通过代理类对所有的请求都进行处理,完成之后,我们只需关注单个 API 的实现,而不用每个都考虑 token 过期,大大地实现解耦操作。
App多个请求token失效的处理逻辑
当集成了Retrofit之后,我们app中的网络请求接口则变成了一个个单独的方法,这时我们需要添加一个全局的token错误抛出机制,来避免每个接口都所需要的token验证处理。
token失效错误抛出
在Retrofit中的Builder中,是通过GsonConvertFactory来做json转成model数据处理的,这里我们就需要重新实现一个自己的GsonConvertFactory,这里主要由三个文件GsonConvertFactory,GsonRequestBodyConverter,GsonResponseBodyConverter,它们三个从中拿过来新建即可。主要我们重写GsonResponseBodyConverter这个类中的convert的方法,这个方法主要将ResponseBody转换我们需要的Object,这里我们通过拿到我们的token失效的错误信息,然后将其以一个指定的Exception的信息抛出。
GsonConverterFactory代码如下:
修改的地方:
1.修改 GsonConverterFactory 中,生成 GsonResponseBodyConverter 的方法:
public Converter responseBodyConverter(final Type type, Annotation[] annotations, Retrofit retrofit) {
Type newType = new ParameterizedType() {
public Type[] getActualTypeArguments() {
return new Type[] { type };
public Type getOwnerType() {
public Type getRawType() {
return ApiModel.
TypeAdapter adapter = gson.getAdapter(TypeToken.get(newType));
return new GsonResponseBodyConverter&&(adapter);
可以看出我们这里对 type 类型,做以包装,让其重新生成一个类型为 ApiModel 的新类型。因为我们在写接口代码的时候,都以真正的类型 type 来作为返回值的,而不是 ApiModel。
2.GsonResponseBodyConverter的处理 它的修改,则是要针对返回结果,做以异常的判断并抛出,主要看其的 convert方法:
public Object convert(ResponseBody value) throws IOException {
ApiModel apiModel = (ApiModel) adapter.fromJson(value.charStream());
if (apiModel.errorCode == ErrorCode.TOKEN_NOT_EXIST) {
throw new TokenNotExistException();
} else if (apiModel.errorCode == ErrorCode.TOKEN_INVALID) {
throw new TokenInvalidException();
} else if (!apiModel.success) {
// TODO: 16/8/21 handle the other error.
} else if (apiModel.success) {
return apiModel.
} finally {
value.close();
当服务器错误信息的时候,同样也是一个 model,不同的是 success 为 false,并且含有 error_code的信息。所以我们需要针对 model 处理的时候,做以判断。主要修改的地方就是 retrofit 的 GsonConvertFactory,这里不再通过 gradle 引入,直接把其源码中的三个文件添加到咱们的项目中。
首先提及的一下是对统一 model 的封装,如下:
public class ApiModel {
@SerializedName(&error_code&) public int errorC
当正确返回的时候,我们获取到 data,直接给上层;当出错的时候,可以针对 errorCode的信息,做一些处理,让其走最上层调用的 onError 方法。
多请求的API代理
为所有的请求都添加Token的错误验证,还要做统一的处理。借鉴Retrofit创建接口的api,我们也采用代理类,来对Retrofit的API做统一的代理处理。
建立API代理类
public class ApiServiceProxy {
Retrofit mR
ProxyHandler mProxyH
public ApiServiceProxy(Retrofit retrofit, ProxyHandler proxyHandler) {
mRetrofit =
mProxyHandler = proxyH
T getProxy(Class tClass) {
T t = mRetrofit.create(tClass);
mProxyHandler.setObject(t);
return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class[] { tClass }, mProxyHandler);
这样,我们就需要通过ApiServiceProxy中的getProxy方法来创建API请求。另外,其中的ProxyHandler则是实现InvocationHandler来实现。
public class ProxyHandler implements InvocationHandler {
private Object mO
public void setObject(Object obj) {
this.mObject =
public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
Object result =
result = Observable.just(null)
.flatMap(new Func1&() {
public Observable call(Object o) {
checkTokenValid(method, args);
return (Observable) method.invoke(mObject, args);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
return Observable.just(new APIException(-100, &method call error&));
}).retryWhen(new Func1<observable, Observable&() {
public Observable call(Observable observable) {
return observable.
flatMap(new Func1&() {
public Observable call(Throwable throwable) {
Observable x = checkApiError(throwable);
if (x != null)
return Observable.error(throwable);
, Schedulers.trampoline());
}</observable
这里的invoke方法则是我们的重头戏,在其中通过将method.invoke方法包装在Observable中,并添加retryWhen的方法,在retryWhen方法中,则对我们在GsonResponseBodyConverter中暴露出来的错误,做一判断,然后执行重新获取token的操作,这段代码就很简单了。就不再这里细述了。
还有一个重要的地方就是,当token刷新成功之后,我们将旧的token替换掉呢?笔者查了一下,java8中的method类,已经支持了动态获取方法名称,而之前的Java版本则是不支持的。那这里怎么办呢?通过看retrofit的调用,可以知道retrofit是可以将接口中的方法转换成API请求,并需要封装参数的。那就需要看一下Retrofit是如何实现的呢?最后发现重头戏是在Retrofit对每个方法添加的@interface的注解,通过Method类中的getParameterAnnotations来进行获取,主要的代码实现如下:
* Update the token of the args in the method.
private void updateMethodToken(Method method, Object[] args) {
if (mIsTokenNeedRefresh && !TextUtils.isEmpty(GlobalToken.getToken())) {
Annotation[][] annotationsArray = method.getParameterAnnotations();
Annotation[]
if (annotationsArray != null && annotationsArray.length & 0) {
for (int i = 0; i & annotationsArray. i++) {
annotations = annotationsArray[i];
for (Annotation annotation : annotations) {
if (annotation instanceof Query) {
if (TOKEN.equals(((Query) annotation).value())) {
args[i] = GlobalToken.getToken();
mIsTokenNeedRefresh =
这里,则遍历我们所使用的token字段,然后将其替换成新的token.
最上层的代码调用中,添加了两个按钮:
按钮1:获取token
token 获取成功之后,仅仅更新一下全局的token即可。
按钮2:正常的请求
这里为了模拟多请求,这里我直接调正常的请求5次:
为了查看输出,另外对 Okhttp 添加了 HttpLoggingInterceptor 并设置 Body 的 level 输出,用来监测 http 请求的输出。
一切完成之后,先点击获取 token 的按钮,等待30秒之后,再点击正常请求按钮。可以看到如下的输出:
--& GET http://192.168.56.1:8888/request?token=4 http/1.1
--& END GET
--& GET http://192.168.56.1:8888/request?token=4 http/1.1
--& END GET
--& GET http://192.168.56.1:8888/request?token=4 http/1.1
--& END GET
--& GET http://192.168.56.1:8888/request?token=4 http/1.1
--& END GET
--& GET http://192.168.56.1:8888/request?token=4 http/1.1
--& END GET
&-- 200 OK http://192.168.56.1:8888/request?token=4 (8ms)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Transfer-Encoding: chunked
{&success&:false,&error_code&:1001}
&-- END HTTP (35-byte body)
&-- 200 OK http://192.168.56.1:8888/request?token=4 (5ms)
&-- 200 OK http://192.168.56.1:8888/request?token=4 (4ms)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Transfer-Encoding: chunked
--& GET http://192.168.56.1:8888/refresh_token http/1.1
--& END GET
{&success&:false,&error_code&:1001}
&-- END HTTP (35-byte body)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Transfer-Encoding: chunked
&-- 200 OK http://192.168.56.1:8888/request?token=4 (7ms)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
{&success&:false,&error_code&:1001}
Transfer-Encoding: chunked
&-- END HTTP (35-byte body)
{&success&:false,&error_code&:1001}
&-- END HTTP (35-byte body)
&-- 200 OK http://192.168.56.1:8888/refresh_token (2ms)
Content-Type: text/plain
&-- 200 OK http://192.168.56.1:8888/request?token=4 (6ms)
Date: Mon, 22 Aug :09 GMT
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Connection: keep-alive
Transfer-Encoding: chunked
Transfer-Encoding: chunked
{&success&:true,&data&:{&token&:&6&}}
&-- END HTTP (49-byte body)
{&success&:false,&error_code&:1001}
&-- END HTTP (35-byte body)
roxy: Refresh token success, time = 7
--& GET http://192.168.56.1:8888/request?token=6 http/1.1
--& GET http://192.168.56.1:8888/request?token=6 http/1.1
--& END GET
--& END GET
--& GET http://192.168.56.1:8888/request?token=6 http/1.1
--& GET http://192.168.56.1:8888/request?token=6 http/1.1
--& END GET
--& END GET
--& GET http://192.168.56.1:8888/request?token=6 http/1.1
--& END GET
&-- 200 OK http://192.168.56.1:8888/request?token=6 (2ms)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Transfer-Encoding: chunked
{&success&:true,&data&:{&result&:true}}
&-- END HTTP (39-byte body)
&-- 200 OK http://192.168.56.1:8888/request?token=6 (4ms)
&-- 200 OK http://192.168.56.1:8888/request?token=6 (6ms)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Transfer-Encoding: chunked
{&success&:true,&data&:{&result&:true}}
&-- END HTTP (39-byte body)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Transfer-Encoding: chunked
&-- 200 OK http://192.168.56.1:8888/request?token=6 (4ms)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Transfer-Encoding: chunked
{&success&:true,&data&:{&result&:true}}
&-- END HTTP (39-byte body)
&-- 200 OK http://192.168.56.1:8888/request?token=6 (7ms)
Content-Type: text/plain
Date: Mon, 22 Aug :09 GMT
Connection: keep-alive
Transfer-Encoding: chunked
{&success&:true,&data&:{&result&:true}}
&-- END HTTP (39-byte body)
{&success&:true,&data&:{&result&:true}}
&-- END HTTP (39-byte body)
刚发出的5个请求都返回了 token 过期的 error,之后看到一个重新刷新 token 的请求,它成功之后,原先的5个请求又进行了重试,并都返回了成功的信息。
完整代码:
/alighters/AndroidDemos/tree/master/app/src/main/java/com/lighters/demos/token
server代码则是根目录下的 server 文件夹中,测试的时候不要忘启动 server 哦。
以上实现是将token放在在url里面,如果是放在Header里面,怎么实现呢?还是要通过okhttp的拦截器来实现。
1.通过拦截器,获取返回的数据
2.判断token是否过期
3.如果token过期则刷新token
4.使用最新的token,重新请求网络数据
实现如下:
public class TokenInterceptor implements Interceptor {
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
Response response = chain.proceed(request);
if (isTokenExpired(response)) {//根据和服务端的约定判断token过期
//同步请求方式,获取最新的Token
String newSession = getNewToken();
//使用新的Token,创建新的请求
Request newRequest = chain.request()
.newBuilder()
.header(&Cookie&, &JSESSIONID=& + newSession)
//重新请求
return chain.proceed(newRequest);
* 根据Response,判断Token是否失效
* @param response
private boolean isTokenExpired(Response response) {
if (response.code() == 404) {
* 同步请求方式,获取最新的Token
private String getNewToken() throws IOException {
// 通过一个特定的接口获取新的token,此处要用到同步的retrofit请求
Response_Login loginInfo = CacheManager.restoreLoginInfo(BaseApplication.getContext());
String username = loginInfo.getUserName();
String password = loginInfo.getPassword();
Call call = WebHelper.getSyncInterface().synclogin(new Request_Login(username, password));
loginInfo = call.execute().body();
loginInfo.setPassword(password);
CacheManager.saveLoginInfo(loginInfo);
return loginInfo.getSession();
添加拦截器:
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(new TokenInterceptor())
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467142',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'扫描下载MIUI论坛APP
在线时间1 小时
积分 23, 距离下一级还需 27 积分
积分 23, 距离下一级还需 27 积分
发表于 6&天前
红米Note 3G TD
V8.0.1.0.MBECNDG
最近玩黎明之光,用4g数据连接,会提示无法连接服务器,验证token失败什么的。wifi可以登录 ,热点可以登录,就是数据连接不行。我网络没我问题,看电视下电影都可以
最近几天一直登不进去,一直用wifi
遇到的人越多,MIUI开发组会越关注
分享到微信朋友圈
打开微信,点击底部的“发现”,使用 “扫一扫” 即可将网页分享到我的朋友圈。
Copyright (C) 2016 MIUI
京ICP备号 | 京公网安备34号 | 京ICP证110507号关于play token_playtoken手游吧_百度贴吧
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&签到排名:今日本吧第个签到,本吧因你更精彩,明天继续来努力!
本吧签到人数:0可签7级以上的吧50个
本月漏签0次!成为超级会员,赠送8张补签卡连续签到:天&&累计签到:天超级会员单次开通12个月以上,赠送连续签到卡3张
关注:2贴子:
关于play token
這间公司叫gtoken,公司老板叫Ivan Lee,是同年跟孙燕姿一起得奖亚洲十大杰出青年,由于开创泰国料理品牌连锁100多间,而后出售70~80的股份之后成为亿万美金富翁,由于从小玩游戏长大,所以致力于游戏改变生活创造收入的想法,而在三年前开创并投入gtoken手游制作,venvici是gtoken的市场运营商,也是gtoken交叉持股的股东,在今年的11月10号正式运用经济商代理的模式推广运营东南亚区域中文版手机游戏,11月26号并在台湾发布第一款手机游戏,《战斗魔珠》中文版由于一般下载量的渠道是透过安卓商店或Apple app store,而游戏商需要花大量的上架费及广告明星代言的高昂支出,故gtoken用经济商运营模式,可将手游的曝光及下载推向跟忠实的客户端及玩家以达到四赢的局面!?手机游戏?传统模式:公司花钱请明星代言,花费巨额广告费,百姓是消费者,给公司贡献业绩,钱?公司赚?新型的模式:公司把请明星代言的费用,给到我们这些经销商,我们用口碑的形式给公司宣传,即是消费者,也是经营者,赚取宣传费4?1日,正式上线,空白市场才刚刚开始,现在又这样一个让你参与利润分红的机会,你愿不愿意把握[勾引][新加坡落地的合法公司的结盟:成立3年的GToken是游戏平台,成立2年的VV是直销公司,运用了“只要分享不用推销”的营运模式来达到四赢!12月中文游戏陆续上架,势必引爆华文市场--台湾、香港、澳门、中国、新加坡、马来西亚!最低100美元起的加盟,人人可为。让过去一样的分享却获得不ㄧ样的回报。安全无比的金流:入金:线上刷卡。每日业绩午夜12点系统结算。出金:专用Master提领,每月两次。申请卡片需支付45美元,、10000配套则优惠免收。加盟商推广奖金:小动态:分享平台游戏QRcode给朋友,朋友下载后,VV发1美元; 朋友打开游戏.试玩.给五星的评等,VV发5美元。这位已链接该游戏的朋友日后在该游戏储值的任何金额,VV发10%。大动态:1.推荐奖励奖,高达50%2.组织发展奖,ABC3区,依加盟等级得6%~10%,惊人的三个双轨效应。3.汽车奖励,连续三个月组织发展奖累计达十万美元,公司发出五万美元用于购买车。4.领导奖,按每月获得的组织发展奖金来订星等1-5星核发。金流安全没收现,没拨返利不算投资。Playtoken是一个新加坡的手游开发商,在这个平台有它所有最新和所有开发的游戏。成为Playtoken的代理商,推广拿大量奖金,我发现透过网络赚钱的极好方法。安全性:Playtoken为新加坡进入台湾的公司,新加坡本身是个很守法制的国家。时机:2015/11月才进入台湾,市场正起步,还没多少人做。。。当然身在大陆的朋友现在只能做台湾的推广来注册,。前提会使用VPN翻墙,到时候大陆开放,可用台湾的注册链接继续推广。想知道大陆什么时候开放吗? 三月底 ,时间不等人.它势必引爆中国内地市场,背后巨大的商机,丰厚的奖金制度,现在就分享给你,最低100美金加盟,人人可为。安全无比的现金流:入金:线上刷卡,每日业绩午夜12点系统结算。出金:专用Master提现,每月两次,申请卡片需要支付45美元
创维OLED-S9D
贴吧热议榜
使用签名档&&
保存至快速回贴}

我要回帖

更多关于 微信access token过期 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信