【问题标题】:AsynTask cancel long running operation like HttpGetAsyncTask 取消长时间运行的操作,如 HttpGet
【发布时间】:2013-02-27 23:21:45
【问题描述】:

我见过几个other post 关于取消 AsynTask,但我认为它们中的任何一个都不能解决问题。想象一下这个场景:

public class TestTask extends AsyncTask<Void, Void, Object> {

@Override
protected void onCancelled(Object result) {
    running = false;
    Log.i("Test", "onCancelled");
}

@Override
protected Object doInBackground(Void... params) {
        try {
            Log.i("Test", "cancelling");
            cancel(true);
            Thread.sleep(5000);
            Log.i("Test", "Past sleep");
        } catch (InterruptedException e) {
            Log.i("Test", "InterruptedException", e);
        }

    return null;
}

}

假设我想在下载过程中取消这个长达 20 秒的长请求,例如,如果服务器对 json 请求的响应速度很慢。所以 Thread.sleep(5000) 可能是我想取消的 HttpGet 请求。但是,取消方法被标记为最终方法,因此我无法覆盖它并调用 get.abort()。 onCancel 钩子发生在 doInBackground 之后并返回 UI 线程。一旦 HttpGet 请求开始,检查 isCancel 对我没有任何好处。

我解决这个问题的方法是在我的 AsynTask 上创建一个 abort() 方法,然后调用它。

public void abort() {
   get.abort();
   cancel(true);
}

但这似乎有点违背 Android 的原则。有没有更好的方法来取消请求?

【问题讨论】:

  • 您是否还需要从InputStream 中读取字节以进行连接?您只需在流上调用read 时检查任务isCancelled,与您链接到的问题的答案中显示的完全相同。取消任务后,它将立即终止下载。如果您指的是等待超时,您可以在HttpUrlConnection 上使用setReadTimeout
  • 没有。我说的是想要使用 HttpGet.abort()。使用 httpClient.execute(HttpGet) 时使用 AsynTask 时无法调用此方法。如果我在“执行”之后检查 isCancelled() 并且执行已经完成。我想中止下载,而不仅仅是停止它之后的逻辑。
  • @ebarrenchea 有正确的答案,但没有提到您应该在doInBackground() 中检查isCancelled(),理想情况下会在其中读取流。请参阅cancelling an AsyncTask 部分。
  • 这不会取消Http请求。它只会停止进一步执行。

标签: android android-asynctask


【解决方案1】:

docs for HttpGet 中没有提到线程安全,因此以您描述的方式调用abort() 可能会导致不希望的结果(充其量)。你可以做的是在你的AsyncTask的构造函数中传递一个HttpGet对象,或者通过一个setter(只要你在调用AsyncTask.execute()之前这样做)。

如果任务已被取消,您必须定期检查 doInBackground() 内部:

@Override
protected Object doInBackground(Void... params) {
        // start GET request
        try {
            Log.i("Test", "cancelling");
            cancel(true);
            Thread.sleep(5000);
            Log.i("Test", "Past sleep");

            if ( this.isCancelled() ) {
               // abort GET request and/or stop doing other work
               return null;
            }
            else {
                // do what ever work you need to
            }
        } catch (InterruptedException e) {
            Log.i("Test", "InterruptedException", e);
        }

    return null;
}

Source.

当您觉得任务应该停止时,您可以随时致电AsyncTask.cancel(true) (AsyncTask.cancel())

【讨论】:

  • 感谢您的评论。检查 isCancelled 只会停止未来的代码执行。它不会取消当前请求。假设 Get 需要 20 秒。使用此解决方案,无法取消该 20 秒的时间。通过 setter 或构造函数传递 HttpGet 仍会导致您建议的相同线程问题。不过,我真的不担心这个。
  • @user123321 你可以在其他地方保留一个计时器,当它完成时,检查操作是否完成,如果没有,取消它。取消AsyncTask 的机制仍然相同。
【解决方案2】:

我不确定这是否适用,但是您可以使用执行下载任务的执行程序服务创建另一个线程,并继续检查 doInBackground() 内的 isCancelled() 或直到未来的对象返回,无论发生什么第一:

@Override
protected Object doInBackground(Void... params)
{
    Callable<Void> downlaodTask = new Callable<Void>()
    {
        @Override
        public Void call() throws Exception
        {
            // download task here

            return null;
        }
    };

    ExecutorService executor = Executors.newSingleThreadExecutor();
    Future<Void> future = executor.submit(downlaodTask);

    while(true) // check every second
    {
        try
        {
            future.get(1, TimeUnit.SECONDS); // wait until the download task finishes with 1 second as a timeout
            break;
        }
        catch(TimeoutException e)
        {
            if(isCancelled())
            {
                executor.shutdownNow(); // or abort() or both
                break;
            }
        }
    }

    return null;
}

【讨论】:

    猜你喜欢
    • 2013-01-21
    • 2012-09-29
    • 2012-04-03
    • 1970-01-01
    • 1970-01-01
    • 2021-10-29
    • 2016-02-22
    • 2011-03-03
    • 1970-01-01
    相关资源
    最近更新 更多