Using Square's Retrofit Client, is it possible to cancel an in progress request? If so how?

前端 未结 7 2022
隐瞒了意图╮
隐瞒了意图╮ 2020-12-25 14:07

I\'m using Square\'s Retrofit Client to make short-lived json requests from an Android App. Is there a way to cancel a request? If so, how?

相关标签:
7条回答
  • 2020-12-25 14:51

    Now there is an easy way in latest version of Retrofit V 2.0.0.beta2. Can implement retry too.
    Take a look here How to cancel ongoing request in retrofit when retrofit.client.UrlConnectionClient is used as client?

    0 讨论(0)
  • 2020-12-25 14:51

    According to the Retrofit 2.0 beta 3 changelog via link https://github.com/square/retrofit/releases/tag/parent-2.0.0-beta3

    New: isCanceled() method returns whether a Call has been canceled. Use this in onFailure to determine whether the callback was invoked from cancelation or actual transport failure.

    This should make stuff easier.

    0 讨论(0)
  • 2020-12-25 14:53

    I've implemented cancelable callback class based on answer https://stackoverflow.com/a/23271559/5227676

    public abstract class CancelableCallback<T> implements Callback<T> {
    
        private static List<CancelableCallback> mList = new ArrayList<>();
    
        private boolean isCanceled = false;
        private Object mTag = null;
    
        public static void cancelAll() {
            Iterator<CancelableCallback> iterator = mList.iterator();
            while (iterator.hasNext()){
                iterator.next().isCanceled = true;
                iterator.remove();
            }
        }
    
        public static void cancel(Object tag) {
            if (tag != null) {
                Iterator<CancelableCallback> iterator = mList.iterator();
                CancelableCallback item;
                while (iterator.hasNext()) {
                    item = iterator.next();
                    if (tag.equals(item.mTag)) {
                        item.isCanceled = true;
                        iterator.remove();
                    }
                }
            }
        }
    
        public CancelableCallback() {
            mList.add(this);
        }
    
        public CancelableCallback(Object tag) {
            mTag = tag;
            mList.add(this);
        }
    
        public void cancel() {
            isCanceled = true;
            mList.remove(this);
        }
    
        @Override
        public final void success(T t, Response response) {
            if (!isCanceled)
                onSuccess(t, response);
            mList.remove(this);
        }
    
        @Override
        public final void failure(RetrofitError error) {
            if (!isCanceled)
                onFailure(error);
            mList.remove(this);
        }
    
        public abstract void onSuccess(T t, Response response);
    
        public abstract void onFailure(RetrofitError error);
    }
    

    Usage example

    rest.request(..., new CancelableCallback<MyResponse>(TAG) {
        @Override
        public void onSuccess(MyResponse myResponse, Response response) {
            ...
        }
    
        @Override
        public void onFailure(RetrofitError error) {
           ...
        }
    });
    
    // if u need to cancel all
    CancelableCallback.cancelAll();
    // or cancel by tag
    CancelableCallback.cancel(TAG);
    
    0 讨论(0)
  • 2020-12-25 14:53

    This is for retrofit 2.0, the method call.cancel() is there which cancels the in-flight call as well. below is the document definition for it.

    retrofit2.Call
    
    public abstract void cancel()
    

    Cancel this call. An attempt will be made to cancel in-flight calls, and if the call has not yet been executed it never will be.

    0 讨论(0)
  • 2020-12-25 14:57

    For canceling async Retrofit request, you can achieve it by shutting down the ExecutorService that performs the async request.

    For example I had this code to build the RestAdapter:

    Builder restAdapter = new RestAdapter.Builder();
    restAdapter.setEndpoint(BASE_URL);
    restAdapter.setClient(okClient);
    restAdapter.setErrorHandler(mErrorHandler);
    mExecutorService = Executors.newCachedThreadPool();
    restAdapter.setExecutors(mExecutor, new MainThreadExecutor());
    restAdapter.setConverter(new GsonConverter(gb.create()));
    

    and had this method for forcefully abandoning the requests:

    public void stopAll(){
       List<Runnable> pendingAndOngoing = mExecutorService.shutdownNow();
       // probably await for termination.
    }
    

    Alternatively you could make use of ExecutorCompletionService and either poll(timeout, TimeUnit.MILISECONDS) or take() all ongoing tasks. This will prevent thread pool not being shut down, as it would do with shutdownNow() and so you could reuse your ExecutorService

    Hope it would be of help for someone.

    Edit: As of OkHttp 2 RC1 changelog performing a .cancel(Object tag) is possible. We should expect the same feature in upcoming Retrofit:

    You can use actual Request object to cancel it

    okClient.cancel(request);

    or if you have supplied tag to Request.Builder you have to use

    okClient.cancel(request.tag());

    All ongoing, executed or pending requests are queued inside Dispatcher, okClient.getDispatcher(). You can call cancel method on this object too. Cancel method will notify OkHttp Engine to kill the connection to the host, if already established.

    Edit 2: Retrofit 2 has fully featured canceling requests.

    0 讨论(0)
  • 2020-12-25 15:07

    I might be a bit late, but I've possibly found a solution. I haven't been able to prevent a request from being executed, but if you're satisfied with the request being performed and not doing anything, you might check this question and answer, both made by me.

    0 讨论(0)
提交回复
热议问题