Rxjava+Retrofit实现全局过期token自动刷新

正文是运用RxJava +
Retrofit来实现网络请求的包裹的,则根本钻探这种状态的落实;一般的写法,则着重是在回调中,做一些截留的论断,这里就不叙述了。

单个请求添加token失效的论断

再利用Rxjava的时候,针对单个API出错,再展开重试机制,这里应该运用的操作符是retryWhen,
通过检测固定的错误音信,然后开展retryWhen中的代码,执行重试机制。这里有个很好的例证,就是扔物线写的RxJava山姆ples中涉及的非五次token的demo。接下来,首要以中间的demo为例,提一下retryWhen的用法。

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

Observable.just(null)
  .flatMap(new Func1<Object, Observable<FakeThing>>() {
      @Override
      public Observable<FakeThing> call(Object o) {
      return cachedFakeToken.token == null
      ? Observable.<FakeThing>error(new NullPointerException("Token is null!"))
      : fakeApi.getFakeData(cachedFakeToken);
      }
      })
.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 IllegalArgumentException || throwable instanceof NullPointerException) {
        return fakeApi.getFakeToken("fake_auth_code")
        .doOnNext(new Action1<FakeToken>() {
            @Override
            public void call(FakeToken fakeToken) {
            tokenUpdated = true;
            cachedFakeToken.token = fakeToken.token;
            cachedFakeToken.expired = fakeToken.expired;
            }
            });
        }
        return Observable.just(throwable);
        }
        });
    }
})

代码中retryWhen执行体中,首要对throwable做的判定是检测是否为NullPointerExceptionIllegalArgumentException,其中前者的抛出是在flatMap的代码体中,当用户的token为空抛出的,而IllegalArgumentException是在怎么时候抛出来的呢?而retryWhen中的代码体还有fakeApi.getFakeData的调用,看来就是在它里面抛出的,来看一下他的代码:

public Observable<FakeThing> getFakeData(FakeToken fakeToken) {
  return Observable.just(fakeToken)
    .map(new Func1<FakeToken, FakeThing>() {
        @Override
        public FakeThing call(FakeToken fakeToken) {
        ...
        if (fakeToken.expired) {
        throw new IllegalArgumentException("Token expired!");
        }

        FakeThing fakeData = new FakeThing();
        fakeData.id = (int) (System.currentTimeMillis() % 1000);
        fakeData.name = "FAKE_USER_" + fakeData.id;
        return fakeData;
        }
        });
}

此处的代码示例中可以看出,当fakeToken失效的时候,则抛出了从前涉嫌的要命。

故而,对token失效的错误音讯,大家需要把它以一贯的error跑出去,然后在retryWhen中开展拍卖,针对token失效的不当,执行token重新刷新的逻辑,而此外的谬误,必须以Observable.error的形式抛出来,不然它继续执行此前的代码体,陷入一个死循环。

token失效错误抛出

在Retrofit中的Builder中,是通过GsonConvertFactory来做json转成model数据处理的,这里我们就需要再行实现一个友好的GsonConvertFactory,那里最紧要由多少个文本GsonConvertFactory,GsonRequestBodyConverter,GsonResponseBodyConverter,它们两个从源码中拿过来新建即可。首要大家重写GsonResponseBodyConverter那么些类中的convert的办法,那些办法紧要将ResponseBody改换我们需要的Object,这里我们经过得到大家的token失效的错误消息,然后将其以一个点名的Exception的消息抛出。

后记

这边,整个完整的代码没有交给,不过思路走下去或者很清楚的。笔者这里的代码是整合了Dagger2一起来完成的,可是代码是一步步两全的。其余,大家依然有众多点可以扩张的,例如,将刷新token的代码变成同步块,只同意单线程的造访,这就交由读者们去一步步做到了。

PS: 更新了完全的Demo,
地址:RxJava+Retrofit兑现全局过期token自动刷新Demo篇

PS:
转载请注脚初稿链接

六个请求token失效的处理逻辑

当集成了Retrofit之后,我们的网络请求接口则变为了一个个独立的办法,这时大家需要加上一个大局的token错误抛出,之后还得需要对持有的接口做一个统一的retryWhen的操作,来避免每个接口都所需要的token验证处理。

俺们在访问户端的规划实现底层网络架构时候,日常不可避免的一个问题:token的可行认证,即便token过期,则需要先举行refresh
token的操作,假设执行refresh
token也行不通,则需要用户再实践登陆的长河中;而这多少个refresh
token的操作,按理来说,对用户是不可见的。这样的话,我们应当是怎么解决这多少个问题吧?

多请求的API代理

为具有的央浼都添加Token的失实验证,还要做统一的拍卖。借鉴Retrofit创造接口的api,大家也接纳代理类,来对Retrofit的API做统一的代办处理。

  • 建立API代理类

public class ApiServiceProxy {

    Retrofit mRetrofit;

    ProxyHandler mProxyHandler;

    public ApiServiceProxy(Retrofit retrofit, ProxyHandler proxyHandler) {
        mRetrofit = retrofit;
        mProxyHandler = proxyHandler;
    }

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

这么,大家就需要通过ApiService(Service)Proxy中的getProxy方法来创立API请求。其它,其中的ProxyHandler则是兑现InvocationHandler来实现。

public class ProxyHandler implements InvocationHandler {

    private Object mObject;

    public void setObject(Object obj) {
        this.mObject = obj;
    }

    @Override
    public Object invoke(Object proxy, final Method method, final Object[] args) throws Throwable {
        Object result = null;
        result = Observable.just(null)
            .flatMap(new Func1<Object, Observable<?>>() {
                @Override
                public Observable<?> call(Object o) {
                    try {
                        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<? extends Throwable>, Observable<?>>() {
                             @Override
                             public Observable<?> call(Observable<? extends Throwable> observable) {
                                 return observable.
                                     flatMap(new Func1<Throwable, Observable<?>>() {
                                                 @Override
                                                 public Observable<?> call(Throwable throwable) {
                                                     Observable<?> x = checkApiError(throwable);
                                                     if (x != null) return x;
                                                     return Observable.error(throwable);
                                                 }
                                             }

                                     );
                             }
                         }

                , Schedulers.trampoline());
        return result;
        }
  }

这里的invoke情势则是我们的本位,在里边通过将method.invoke办法包装在Observable中,并添加retryWhen的艺术,在retryWhen方法中,则对我们在GsonResponseBodyConverter中暴露出来的谬误,做一断定,然后实施重新拿到token的操作,这段代码就很粗略了。就不再这里细述了。

再有一个重大的地方就是,当token刷新成功将来,大家将旧的token替换掉吧?笔者查了一下,java8中的method类,已经辅助了动态获取格局名称,而此前的Java版本则是不援助的。这这里如何做呢?通过看retrofit的调用,可以清楚retrofit是足以将接口中的方法转换成API请求,并需要封装参数的。这就需要看一下Retrofit是什么样兑现的吧?最终发现主题是在Retrofit对各类方法添加的@interface的笺注,通过Method类中的getParameterAnnotationsJava,来进展获取,紧要的代码实现如下:

Annotation[][] annotationsArray = method.getParameterAnnotations();
Annotation[] annotations = null;
Annotation annotation = null;
if (annotationsArray != null && annotationsArray.length > 0) {
  for (int i = 0; i < annotationsArray.length; i++) {
    annotations = annotationsArray[i];
    for (int j = 0; j < annotations.length; j++) {
      annotation = annotations[j];
      if (annotation instanceof Query) {
        if (ACCESS_TOKEN_KEY.equals(((Query) annotation).value())) {
          args[i] = newToken;
        }
      }
    }
  }
}

此间,则遍历我们所接纳的token字段,然后将其替换成新的token.

相关文章