【问题标题】:android autocomplete AsyncTask delayandroid自动完成AsyncTask延迟
【发布时间】:2016-07-08 13:20:03
【问题描述】:

我正在实现一个位置建议活动,当用户在文本视图中键入时,它会从外部服务器填充建议。每次文本视图中的文本发生更改时,我都使用AsyncTask 来获取建议。当输入一个新字母时,我们取消已经存在的任务并执行一个新的。大多数情况下doInBackground 在调用execute 后立即启动,但其他时候可能需要几秒钟。 (一旦 doInBackground 启动,性能就很好了。)

设置监听器:

private void init() {
    // respond to any text change
    textView.addTextChangedListener(new TextWatcher() {
        @Override
        public void onTextChanged(final CharSequence s, int start, int b, int c) {
            showSuggestions(s.toString());
        }
    });
}

这里我们开始一个新的任务,取消上一个:

private void showSuggestions(String query) {
    // suggestionsTask is an instance variable of the activity
    if (suggestionsTask != null) {
        suggestionsTask.cancel(true);
    }

    suggestionsTask = new AsyncTask<String, Void, List<Suggestion>>() {
        @Override
        protected void onPostExecute(List<Suggestion> resultList) {
            // set suggestions with adapter - CHANGES STATE
        }

        @Override
        protected List<Suggestion> doInBackground(String... query) {
            // one local db call for recent searches - DOES NOT CHANGE STATE
            // one network call to external server - DOES NOT CHANGE STATE
            // return results
        }
    };

    suggestionsTask.execute(query);
}

是否有更好的线程机制可以用于此?你知道为什么execute和doInBackground之间会有延迟吗?

【问题讨论】:

  • 可能是任务被序列化了,前一个任务需要时间取消。
  • 听起来很有可能。你知道如何解决这个问题吗?

标签: android android-asynctask autocomplete


【解决方案1】:

来自AsyncTask reference

可以通过调用 cancel(boolean) 随时取消任务。调用此方法将导致对 isCancelled() 的后续调用返回 true。调用此方法后,将在 doInBackground(Object[]) 返回后调用 onCancelled(Object),而不是 onPostExecute(Object)。 为确保尽快取消任务,如果可能(例如在循环内),您应该始终定期从 doInBackground(Object[]) 检查 isCancelled() 的返回值。

因此,除非您手动检查是否在 doInBackground 中定期设置了 isCancelled(),否则您实际上并没有取消 doInBackground() 步骤。在大多数 Android 版本中,所有 AsyncTask 共享一个线程,因此必须先完成一个线程,然后才能开始下一个线程。这就是您延迟的原因,但我没有从您的 doInBackground() 代码中获得足够的信息(即您没有发布代码),无法就在哪里检查 isCancelled() 提出建议。

如果由于某种原因无法取消上一个任务,您还可以尝试让您的 AsyncTasks 并行执行,使用 executeOnExecutor(java.util.concurrent.Executor, Object[])THREAD_POOL_EXECUTOR,正如同一文档所建议的那样,但您正在尝试这样做似乎会导致一些令人沮丧的线程问题,这可能会比你现在遇到的更糟糕。

【讨论】:

  • "... THREAD_POOL_EXECUTOR 正如相同的文档所暗示的那样,但是您尝试执行的操作似乎可能会导致一些令人沮丧的线程问题”。如果我在每次新执行之前取消,因此只有最新的任务在 PostExecute 上执行,怎么会有线程问题?我的帖子不包含此内容,但我试图暗示唯一被修改的全局状态是 onPostExecute:“使用适配器设置建议”
  • 我想到的最简单的例子是,如果您在不同的线程上执行多个 AsyncTask,则无法保证您开始的第一个任务将是第一个完成的任务。因此,您最终可能会从一个缓慢但正确的解决方案转向一个更快但错误的解决方案。当然,您仍然可以使用线程池执行器,但它会带来难以准备的问题,而且更烦人的是,它不会一直失败。在我看来,坚持使用默认执行程序并确保您实际上使用 doInBackground() 中的循环取消了上一个任务会容易得多。
  • 在一般情况下我同意你的观点,但鉴于我在问题中包含的代码,我认为你的评估在这里过于谨慎/错误。 “如果您在不同的线程上执行多个 AsyncTask,则无法保证您开始的第一个将是第一个完成的”我不关心第一个完成的第一个 - 只是最后一个修改适配器最后。如果我使用 THREAD_POOL_EXECUTOR,我对我的解决方案可能出错的可能性持开放态度,但我没有在你的 cmets 中看到证据。请解决我之前的问题:“怎么会有... onPostExecute?”
  • 是的,“不能保证第一个会先完成”我的意思是不能保证最后一个开始的会最后完成。
  • 或者这可能是混淆:您说“如果我在每次新执行之前取消,因此只有最新的任务在 PostExecute 上执行,怎么会有线程问题?”但正如我在回答中所说,当您调用 AsyncTask.cancel() 时,您实际上并没有取消任务。第一个任务的 doInBackground() 实际上会完成所有操作,即使在您调用了 cancel() 之后也是如此,除非您在 doInBackground() 中的循环中手动检查它并在看到它已被取消时保释。
猜你喜欢
  • 1970-01-01
  • 2015-02-02
  • 2016-07-28
  • 1970-01-01
  • 2021-04-14
  • 1970-01-01
  • 1970-01-01
  • 2012-08-01
  • 1970-01-01
相关资源
最近更新 更多