package com.chiclaim.rxjava.operator; import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import com.chiclaim.rxjava.BaseFragment; import com.chiclaim.rxjava.R; import com.chiclaim.rxjava.api.ApiServiceFactory; import com.chiclaim.rxjava.api.NetErrorType; import com.chiclaim.rxjava.api.UserApi; import com.chiclaim.rxjava.exception.AccessDenyException; import com.chiclaim.rxjava.model.AuthToken; import retrofit.client.Response; import retrofit.mime.TypedByteArray; import rx.Observable; import rx.Observer; import rx.Subscriber; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Func1; import rx.schedulers.Schedulers; /** * Created by chiclaim on 2016/03/24 */ public class HttpWithTokenFragment extends BaseFragment { private final UserApi userApi = ApiServiceFactory.createService(UserApi.class); private TextView tvLogs; private boolean loading; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_http_token, container, false); } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); tvLogs = (TextView) view.findViewById(R.id.tv_logs); view.findViewById(R.id.btn_request).setOnClickListener(this); requestUserInfo(); } public Observable<AuthToken> createTokenObvervable() { return Observable.create(new Observable.OnSubscribe<AuthToken>() { @Override public void call(Subscriber<? super AuthToken> observer) { try { if (!observer.isUnsubscribed()) { appendText(tvLogs, "God!!! Token is out of date. \nstart refresh token......"); observer.onNext(userApi.refreshToken()); observer.onCompleted(); } } catch (Exception e) { observer.onError(e); } } }).subscribeOn(Schedulers.io()); } private <T> Func1<Throwable, ? extends Observable<? extends T>> refreshTokenAndRetry(final Observable<T> toBeResumed) { return new Func1<Throwable, Observable<? extends T>>() { @Override public Observable<? extends T> call(Throwable throwable) { throwable.printStackTrace(); // Here check if the error thrown really is a 401 if (isHttp401Error(throwable)) { return createTokenObvervable().flatMap(new Func1<AuthToken, Observable<? extends T>>() { @Override public Observable<? extends T> call(AuthToken token) { appendText(tvLogs, "refresh token success,token's validity is 10s\nResume last request"); return toBeResumed; } }); } // re-throw this error because it's not recoverable from here return Observable.error(throwable); } public boolean isHttp401Error(Throwable throwable) { return throwable instanceof AccessDenyException; } }; } private void requestUserInfo() { appendText(tvLogs, "start to request user info"); loading = true; Observable<Response> observable = userApi.getUserInfo(); observable.onErrorResumeNext(refreshTokenAndRetry(observable))//also use retryWhen to implement it .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer<Response>() { @Override public void onCompleted() { loading = false; appendText(tvLogs, "task completed-----"); //hideLoadingDialog(); } @Override public void onError(Throwable t) { //hideLoadingDialog(); t.printStackTrace(); loading = false; appendText(tvLogs, t.getClass().getName() + "\n" + t.getMessage()); NetErrorType.ErrorType error = NetErrorType.getErrorType(t); appendText(tvLogs, error.msg); } public void onNext(Response response) { String content = new String(((TypedByteArray) response.getBody()).getBytes()); appendText(tvLogs, "receiver data: " + content); } }); } @Override public void onClick(View v) { super.onClick(v); switch (v.getId()) { case R.id.btn_request: if (!loading) { tvLogs.append("\n\n--------------------------------"); requestUserInfo(); } break; } } }