Android LiveData + Retrofit 取消请求

Android LiveData + Retrofit 取消请求(一)


我们先看看OkHttp是怎么取消请求的

OkHttp 中取消请求
OkHttp 创建、发送请求
        //创建OkHttpClient
         OkHttpClient   mOkHttpClient = new OkHttpClient().newBuilder().build();
         //创建Request,添加tag标记,tag 类型为Object,用tag来取消请求
         Request.Builder builder = new Request.Builder();
         Request request =  builder.url("requestUrl").tag("request").build();
         //创建准备要执行请求的Call
        final Call call = mOkHttpClient.newCall(request);
         //调度发起异步请求
         call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {

            }
        });
取消请求
      /**
         * 根据tag取消请求
         * @param tag
         */
   public void cancelTag(Object tag) {
         if (mOkHttpClient == null)
            return;
        for (Call call : mOkHttpClient.dispatcher().queuedCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
        for (Call call : mOkHttpClient.dispatcher().runningCalls()) {
            if (tag.equals(call.request().tag())) {
                call.cancel();
            }
        }
    }

调用cancelTag(“request”)取消上面创建的请求

看一下cancel的方法

  /** Cancels the request, if possible. Requests that are already complete cannot be canceled. */
  void cancel();

如果可能的话,取消请求。已完成的请求无法取消

我们都知道Retrofit是基于OkHttp的再次封装,Retrofit 怎么取消请求呢?

Retrofit中取消请求

Retrofit并没有显示地提供能访问OkHttp的tag方法,2018年时Retrofit仍未提供直接访问call对象的方法

JakeWharton 提供了一些提示

You might be able to add dummy headers and loop through OkHttp's in-flight Calls and match on them that way though.
您可以添加虚拟的头文件,并循环使用OkHttp的动态调用,然后通过这种方式对它们进行匹配

what mean?

联网查询了下,原来是先在请求interface Service中提供的Call方法添加注解Header,然后在拦截器进行匹配取消

举个栗子

当Activity,Fragment 将要销毁时,取消未完成的请求,就要对页面添加标记,代表着页面生存状态,那就需要一个集合来管理这个标记

创建存放Tag的集合
public class NetTagManager {
    private static ConcurrentHashMap<String, Boolean> actLiveMap = new ConcurrentHashMap<>(); // 标记Activity是否存活

    public static void markPageAlive(String actName) {
        actLiveMap.put(actName, true);
    }

    public static void markPageDestroy(String actName) {
        actLiveMap.put(actName, false);
    }

    public static ConcurrentHashMap<String, Boolean> getActLiveMap() {
        return actLiveMap;
    }
}
在Activity中管理页面状态
private  final String MY_ACT_NAME = getClass().getSimpleName();
 @Override
 protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    NetTagManager.markPageAlive(MY_ACT_NAME);
     //发起请求
     RequestCreator.getRequestService().post(MY_ACT_NAME,...,...).enqueue(...);
  }
  
     @Override
  protected void onDestroy() {
     NetTagManager.markPageDestroy(TAG);
   }

下面就是Retrofit的处理

创建请求Service Interface,添加Head注解
public class Base {
 //定义注解Head的key
 public static final String HEAD_TAG = "TAG_NAME";
}
public interface RequestService {
 
     @FormUrlEncoded
     @POST
    Call<String> post(@Header(Base.HEAD_TAG) String actName, @Url String url, @FieldMap           Map<String, Object> params);  
}  
创建Retrofit对象,并在OkHttpClient中添加Cancel 拦截器
public class RequestCreator {
private static final String BASE_URL = "http://...";  
private static final Retrofit RETROFIT_CLIENT =
              new Retrofit.Builder().baseUrl(BASE_URL).
                      ... 
                      .client(OkHttpHolder.OK_HTTP_CLIENT).build();
 private static  final  class  OkHttpHolder{
    
        private static final int TIME_OUT = 60;
        private static final OkHttpClient OK_HTTP_CLIENT = new OkHttpClient.Builder().
                 connectTimeout(TIME_OUT, TimeUnit.SECONDS)
                .addInterceptor(new CancelInterceptor())
                .build();
  
     }
private static final RequestService REQUEST_SERVICE = RETROFIT_CLIENT.create(RequestService.class);
 public static  RequestService getRequestService(){
        return  REQUEST_SERVICE;
    }    
}

//在拦截器中检查页面的存活情况。检查后,把这个自定义header移除掉
//Cancel interceptor
public class CancelInterceptor implements Interceptor{
    private static final String TAG = CancelInterceptor.class.getSimpleName();
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        String actName = request.header(Base.HEAD_TAG);
        Log.d(TAG,"actName==="+actName);
        if (!TextUtils.isEmpty(actName)){
            Log.d(TAG,"HeadName==="+actName);
            boolean actLive = NetTagManager.getActLiveMap().get(actName);
            if(!actLive){
               chain.call().cancel();
                Log.d(TAG, "取消请求, actName: " + actName);
            }else{
                Log.d(TAG, "发起请求, actName: " + actName);
            }
        }
         Request newRequest = request.newBuilder().removeHeader(Base.HEAD_TAG).build();
        return chain.proceed(newRequest);

    }
}
在Activity中发起请求
 RequestCreator.getRequestService().post(MY_ACT_NAME,...,...).enqueue(...);

还有一种 实现CallAdapter.Factory方法:

来源于retrofit-helper 简洁的封装retrofit

Retrofit 可以添加CallAdapter,根据Retrofit默认的DefaultCallAdapterFactory

,通过添加修改CallAdapter.Factory的实现类,也就是用addCallAdapterFactory()添加一个适配器,如:

private static final Retrofit RETROFIT_CLIENT = 
new Retrofit.Builder().baseUrl(BASE_URL)
.addCallAdapterFactory(ExecutorCallAdapterFactory().create())...;

需要自己实现一个CallAdapterrFactory,在enqueue(Object tag, final Callback callback)中关联tag和Call,

其中Call2实现了retrofit中的Call 并且添加enqueue(Object tag, final Callback callback)方法

public interface Call2<T> extends Call<T> {
    void enqueue(Object tag,Callback<T> callback);
     Call2<T> clone();
}
public class ExecutorCallAdapterFactory extends CallAdapter.Factory {
    @Override
    public CallAdapter<?, ?> get(Type returnType, Annotation[] annotations, Retrofit retrofit) {
         //Call2实现了retrofit中的Call  
        if (getRawType(returnType) != Call2.class) {
            return null;
        }
        if (!(returnType instanceof ParameterizedType)) {
            throw new IllegalArgumentException(
                    "Call return type must be parameterized as Call<Foo> or Call<? extends Foo>");
        }
        final Type responseType = getParameterUpperBound(0, (ParameterizedType) returnType);

        final Executor executor = retrofit.callbackExecutor();
        if (executor == null) {
            throw new NullPointerException("executor is null");
//            executor = new MainThreadExecutor();
        }
        return new CallAdapter<Object, Object>() {
            @Override
            public Type responseType() {
                return responseType;
            }

            @Override
            public Object adapt(Call<Object> call) {
                return executor == null
                        ? call
                        : new ExecutorCallbackCall<>(executor, call);
            }
        };
    }

    static class MainThreadExecutor implements Executor {
        private final Handler handler = new Handler(Looper.getMainLooper());

        @Override
        public void execute(Runnable r) {
            handler.post(r);
        }
    }

    public static Type getParameterUpperBound(int index, ParameterizedType type) {
        Type[] types = type.getActualTypeArguments();
        if (index < 0 || index >= types.length) {
            throw new IllegalArgumentException(
                    "Index " + index + " not in range [0," + types.length + ") for " + type);
        }
        Type paramType = types[index];
        if (paramType instanceof WildcardType) {
            return ((WildcardType) paramType).getUpperBounds()[0];
        }
        return paramType;
    }

    static <T> T checkNotNull(T object, String message) {
        if (object == null) {
            throw new NullPointerException(message);
        }
        return object;
    }

    static final class ExecutorCallbackCall<T> implements Call2<T> {
        final Executor callbackExecutor;
        final Call<T> delegate;

        ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
            this.callbackExecutor = callbackExecutor;
            this.delegate = delegate;
        }

        @Override
        public void enqueue(final Callback<T> callback) {
            throw new UnsupportedOperationException("please call enqueue(Object tag, Callback callback)");
//            checkNotNull(callback, "callback == null");
        }

        @Override
        public boolean isExecuted() {
            return delegate.isExecuted();
        }

        @Override
        public Response<T> execute() throws IOException {
            return delegate.execute();
        }

        @Override
        public void cancel() {
            delegate.cancel();
        }

        @Override
        public boolean isCanceled() {
            return delegate.isCanceled();
        }

        @Override
        public void enqueue(Object tag, final Callback<T> callback) {
            CallManager.getInstance().add(tag != null ? tag : "NO_TAG", this);
            delegate.enqueue(new Callback<T>() {
                @Override
                public void onResponse(Call<T> call, final Response<T> response) {
                    callbackExecutor.execute(new Runnable() {
                        @Override
                        public void run() {
                            if (delegate.isCanceled()) {
                                // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
                                callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
                            } else {
                                callback.onResponse(ExecutorCallbackCall.this, response);
                            }
                        }
                    });
                }

                @Override
                public void onFailure(Call<T> call, final Throwable t) {
                    callbackExecutor.execute(new Runnable() {
                        @Override
                        public void run() {
                            callback.onFailure(ExecutorCallbackCall.this, t);
                        }
                    });
                }
            });

        }

        @SuppressWarnings("CloneDoesntCallSuperClone") // Performing deep clone.
        @Override
        public Call2<T> clone() {
            return new ExecutorCallbackCall<>(callbackExecutor, delegate.clone());
        }

        @Override
        public Request request() {
            return delegate.request();
        }
    }

}

CallManage 管理tag和Call

public interface ActionManager<T> {
    void add(T tag, Call call);
    void remove(T tag);
    void removeAll();
    void cancel(T tag);
    void cancelAll();
}
public class CallManager implements ActionManager<Object> {

    private static final String TAG = CallManager.class.getSimpleName();
    private static CallManager sInstance = null;
        private ArrayMap<Object, Call> maps;
        public static CallManager getInstance() {
            if (sInstance == null) {
                synchronized (CallManager.class) {
                    if (sInstance == null) {
                        sInstance = new CallManager();
                    }
                }
            }
            return sInstance;
        }

        private CallManager() {
            maps = new ArrayMap<>();
        }

    @Override
    public void add(Object tag, Call call) {
        Log.d(TAG,"Add Call=="+tag);
        maps.put(tag, call);
    }

    @Override
    public void remove(Object tag) {
        if (!maps.isEmpty()) {
            maps.remove(tag);
        }
    }

    public void removeAll() {
            if (!maps.isEmpty()) {
                maps.clear();
            }
        }

    @Override
    public void cancel(Object tag) {
        if (maps.isEmpty()) {
            return;
        }
        Log.d(TAG,"cancel Call=="+tag);
        Call call =  maps.get(tag);
        if (call != null) {
            call.cancel();
            remove(tag);
        }
    }
        @Override
        public void cancelAll() {
            if (maps.isEmpty()) {
                return;
            }
            Set<Object> keys = maps.keySet();
            for (Object key : keys) {
                    cancel(key);
            }
         }
}

用法如下:

Retrofit RETROFIT_CLIENT =
        new Retrofit.Builder().baseUrl(BASE_URL).
                .addCallAdapterFactory(ExecutorCallAdapterFactory.create())
               .client(OkHttpHolder.OK_HTTP_CLIENT).build();
@FormUrlEncoded
@POST
Call2<String> postCancel(@Url String url, @FieldMap Map<String, Object> params);
RequestCreator.getRequestService().postCancel().enqueue("request", new Callback<String>() {
    @Override
    public void onResponse(Call<String> call, Response<String> response) {
        
    }

    @Override
    public void onFailure(Call<String> call, Throwable t) {

    }
});

参考:Android OkHttp + Retrofit 取消请求的方法

retrofit-helper 简洁的封装retrofit

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值