新浦京81707con > 注册购买 > Retrofit实现全局过期token自动刷新,Retrofit实现全

原标题:Retrofit实现全局过期token自动刷新,Retrofit实现全

浏览次数:163 时间:2020-03-01

那边的代码示例中得以见见,当fakeToken失效的时候,则抛出了后边涉嫌的特别。

4.增多代理

在使用 Retrofit 的时候,大家都亟需针对各样 API 编写相应的接口代码,最终通过 Retrofit 的 create 方法来完结调用,而那个方式便是因此选拔代理,依照那一个接口方法的各个评释参数,最终三个个独立的完全的 API 调用。

因为大家也亟需对每一种 API 做管理,所以大家也对它的 create 方法做多少个代理的达成,首要运用的代码是 Proxy类的 newProxyInstance方法。

public <T> T getProxy(Class<T> tClass) {
  T t = getRetrofit().create(tClass);
  return (T) Proxy.newProxyInstance(tClass.getClassLoader(), new Class<?>[] { tClass }, new ProxyHandler(t));
}

骨干的代理达成则是那些 ProxyHandler,它是对接口 InvocationHandler 的一个兑现类。理念便是指向 method 的调用,做以 retryWhen 的包裹,在retryWhen 中收获相应的不胜新闻来做拍卖,看 retryWhen 的代码:

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) {
                            Toast.makeText(BaseApplication.getContext(), "Token is not existed!!", Toast.LENGTH_SHORT).show();
                            return Observable.error(throwable);
                        }
                        return Observable.error(throwable);
                    }
                });
            }
        })

那边针对 token 过期的 TokenInvalidException 的不行,试行刷新 token 的操作,刷新 token 的操作则是直接调用 Retrofit 的措施,而无需走代理了。此外它必需是个同步的代码块,重要的代码就不在那贴了,具体的代码见 这里。

互连网央浼

  • 基本操作
    EnclavexJava 和 Retrofit 结合使用最大旨的格式:用 subscribeOn(卡塔尔国 和 observeOn(卡塔尔国 来调整线程,并透过 subscribe()来触发互连网央求的带头。代码差不离方式:
api.getData().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(observer);
  • 转换Map
    有一点点服务端的接口设计,会在回来的数额外层包裹一些附加消息,那些新闻对于调节和测量试验很有用,但地面呈现是用不到的。使用 map()能够把外围的格式剥掉,只留下本地会用到的中坚格式。代码大致格局:
api.getData().map(response -> response.data).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(observer);

自然,map(卡塔尔(قطر‎ 也足以用于基于别的各个要求的格式调换。

  • 压合zip
    局地时候,app 中会须要同有的时候间做客分裂接口,然后将结果糅合后转为统一的格式后输出(举例将第三方广告 API 的广告夹杂进作者阳台再次来到的数量 List 中)。这种相互的异步管理比较费劲,不过用了 zip(卡塔尔(قطر‎之后就能够简单得多。代码差相当的少格局:
Observable.zip(api.getData(),adApi.getAds(), zipFunc()).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(observer);
  • 接连嵌套需要flatMap
    出于安全性、品质等方面包车型地铁寻思,好些个服务器会有一部分接口须要传入 token 技术正确重回结果,而 token 是急需从另七个接口获取的,那就需求接收两步接连的乞请技艺获取数据(①token -> ②目的数据)。使用 flatMap()能够用相比清晰的代码落成这种连接诉求,制止 Callback 嵌套的构造。代码大概形式:
api.getToken().flatMap(token -> api.getData(token)).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(observer);
  • 不当重试retryWhen
    部分 token 实际不是一回性的,而是能够每每利用,直到它超时或被销毁(大多token 都以这么的)。那样的 token 处理起来相比较劳碌:须要把它保存起来,而且在乎识它失效的时候要能力所能达到自动重新得到新的 token 并继续拜候在此之前由于 token 失效而诉讼失败的伸手。纵然项目中有多处的接口央求都急需那样的机关修复机制,使用古板的 Callback 情势须求写出极度复杂的代码。而利用 RxJava ,能够用 retryWhen(卡塔尔(قطر‎ 来轻巧地拍卖那样的主题素材。
@Override
    public Observable<?> apply(@NonNull Observable<? extends Throwable> attempts) throws Exception {
        return attempts
                .flatMap(new Function<Throwable, ObservableSource<?>>() {
                    @Override
                    public ObservableSource<?> apply(@NonNull Throwable throwable) throws Exception {
                        if (  retryCount <= maxRetries) {
                            // When this Observable calls onNext, the original Observable will be retried (i.e. re-subscribed).
                            Log.i(TAG, "get error, it will try after "   retryDelayMillis
                                      " millisecond, retry count "   retryCount);
                            return Observable.timer(retryDelayMillis,
                                    TimeUnit.MILLISECONDS);
                        }
                        // Max retries hit. Just pass the error along.
                        return Observable.error(throwable);
                    }

                });
    }

在Demo中的TokenAdvancedFragment中,可查到如下的代码:

5.代码证实

最上层的代码调用中,增添了四个开关:

  • 按钮1:获取token
@OnClick(R.id.btn_token_get)
public void onGetTokenClick(View v) {
  RetrofitUtil.getInstance()
      .get(IApiService.class)
      .getToken()
      .subscribeOn(Schedulers.io())
      .observeOn(AndroidSchedulers.mainThread())
      .subscribe(new Subscriber<TokenModel>() {
          @Override
          public void onCompleted() {

          }

          @Override
          public void onError(Throwable e) {

          }

          @Override
          public void onNext(TokenModel model) {
              if (model != null && !TextUtils.isEmpty(model.token)) {
                  GlobalToken.updateToken(model.token);
              }
          }
      });
}

token 获取成功未来,仅仅更新一下大局的token就能够。

  • 常规的伸手
    那边为了参照他事他说加以考察多诉求,这里自身直接调平常的乞请5次:
@OnClick(R.id.btn_request)
public void onRequestClick(View v) {
  for (int i = 0; i < 5; i  ) {
      RetrofitUtil.getInstance()
          .getProxy(IApiService.class)
          .getResult(GlobalToken.getToken())
          .subscribeOn(Schedulers.io())
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(new Subscriber<ResultModel>() {
              @Override
              public void onCompleted() {

              }

              @Override
              public void onError(Throwable e) {

              }

              @Override
              public void onNext(ResultModel model) {

              }
          });
  }
}

为了查看输出,别的对 Okhttp 增添了 HttpLoggingInterceptor 并设置 Body 的 level 输出,用来监测 http 央浼的出口。

漫天成功以往,先点击获取 token 的开关,等待30秒未来,再点击平常央求开关。能够见见如下的输出:

 --> GET http://192.168.56.1:8888/request?token=1471774119164 http/1.1
 --> END GET
 --> GET http://192.168.56.1:8888/request?token=1471774119164 http/1.1
 --> END GET
 --> GET http://192.168.56.1:8888/request?token=1471774119164 http/1.1
 --> END GET
 --> GET http://192.168.56.1:8888/request?token=1471774119164 http/1.1
 --> END GET
 --> GET http://192.168.56.1:8888/request?token=1471774119164 http/1.1
 --> END GET
 <-- 200 OK http://192.168.56.1:8888/request?token=1471774119164 (8ms)
 Content-Type: text/plain
 Date: Mon, 22 Aug 2016 00:38: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=1471774119164 (5ms)
 <-- 200 OK http://192.168.56.1:8888/request?token=1471774119164 (4ms)
 Content-Type: text/plain
 Date: Mon, 22 Aug 2016 00:38: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 2016 00:38:09 GMT
 Connection: keep-alive
 Transfer-Encoding: chunked
 <-- 200 OK http://192.168.56.1:8888/request?token=1471774119164 (7ms)
 Content-Type: text/plain
 Date: Mon, 22 Aug 2016 00:38: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=1471774119164 (6ms)
 Date: Mon, 22 Aug 2016 00:38:09 GMT
 Content-Type: text/plain
 Date: Mon, 22 Aug 2016 00:38:09 GMT
 Connection: keep-alive
 Connection: keep-alive
 Transfer-Encoding: chunked
 Transfer-Encoding: chunked
 {"success":true,"data":{"token":"1471826289336"}}
 <-- END HTTP (49-byte body)
 {"success":false,"error_code":1001}
 <-- END HTTP (35-byte body)
roxy: Refresh token success, time = 1471790019657
 --> GET http://192.168.56.1:8888/request?token=1471826289336 http/1.1
 --> GET http://192.168.56.1:8888/request?token=1471826289336 http/1.1
 --> END GET
 --> END GET
 --> GET http://192.168.56.1:8888/request?token=1471826289336 http/1.1
 --> GET http://192.168.56.1:8888/request?token=1471826289336 http/1.1
 --> END GET
 --> END GET
 --> GET http://192.168.56.1:8888/request?token=1471826289336 http/1.1
 --> END GET
 <-- 200 OK http://192.168.56.1:8888/request?token=1471826289336 (2ms)
 Content-Type: text/plain
 Date: Mon, 22 Aug 2016 00:38: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=1471826289336 (4ms)
 <-- 200 OK http://192.168.56.1:8888/request?token=1471826289336 (6ms)
 Content-Type: text/plain
 Date: Mon, 22 Aug 2016 00:38: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 2016 00:38:09 GMT
 Connection: keep-alive
 Transfer-Encoding: chunked
 <-- 200 OK http://192.168.56.1:8888/request?token=1471826289336 (4ms)
 Content-Type: text/plain
 Date: Mon, 22 Aug 2016 00:38: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=1471826289336 (7ms)
 Content-Type: text/plain
 Date: Mon, 22 Aug 2016 00:38: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个央浼又拓宽了重试,并都回到了成功的新闻。一切圆满。

终极,一个总体而又简约的德姆o就完了了,假诺还也会有哪些不领会的伴儿能够加 QQ 群:289926871 来调换。完整的代码这里的包为 token 的布局下,server 代码则是根目录下的 server 文件夹中,测量试验的时候绝不忘记运维 server 哦。

进而,对token失效的错误信息,大家须要把它以一定的error跑出来,然后在retryWhen中张开始拍录卖,针对token失效的乖谬,实施token重新刷新的逻辑,而别的的大错特错,必须以Observable.error的样式抛出来,不然它继续实行在此以前的代码体,陷入八个死循环。

Demo 实现

本文是使用奥迪Q7xJava Retrofit来兑现网络诉求的包装的,则首要商量这种处境的落到实处;日常的写法,则要害是在回调中,做一些阻拦的决断,这里就不汇报了。

3.不当抛出

新莆京线上娱乐,当服务器错误消息的时候,相符也是三个 model,分歧的是 success 为 false,何况带有 error_code的音信。所以大家须求针对 model 管理的时候,做以咬定。主要改善的地点正是 retrofit 的 GsonConvertFactory,这里不再通过 gradle 引进,间接把其源码中的七个公文加多到我们的种类中。

先是提起的一眨眼之间是对统一 model 的包裹,如下:

public class ApiModel<T> {
    public boolean success;
    @SerializedName("error_code") public int errorCode;

    public T data;
}

当正确重临的时候,大家收获到 data,直接给上层;当出错的时候,能够针对 errorCode的信息,做一些拍卖,让其走最上层调用的 onError 方法。

好了,说说作者们那边要校勘的地点:

  • 1.修改 GsonConverterFactory 中,生成 GsonResponseBodyConverter 的方法:
@Override
public Converter<ResponseBody, ?> 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);
}

可以看来大家这边对 type 类型,做以包装,让其再次生成八个系列为 ApiModel 的新类型。因为我们在写接口代码的时候,都是真正的类型 type 来作为重回值的,并非 ApiModel。

  • 2.GsonResponseBodyConverter的处理
    它的匡正,则是要本着重回结果,做以那些的论断并抛出,首要看其的 convert方法:
@Override
public Object convert(ResponseBody value) throws IOException {
  try {
      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.
          return null;
      } else if (apiModel.success) {
          return apiModel.data;
      }
  } finally {
      value.close();
  }
  return null;
}

本文由新浦京81707con发布于注册购买,转载请注明出处:Retrofit实现全局过期token自动刷新,Retrofit实现全

关键词: 新浦京81707con 全局 Rxjava Retrofit 开源项目学习

上一篇:【新京葡娱乐场官网app】其實全片最暖的應該不

下一篇:没有了