2016-03-21 7 views
1

私は、ユーザーがテキストビューで入力するときに、外部サーバーからの提案を入力する場所提案アクティビティを実装しています。テキストビューのテキストが変更されるたびに、AsyncTaskを使用して候補を取得しています。新しい文字が入力されると、すでに存在するタスクをキャンセルして新しいタスクを実行します。ほとんどの場合、は、executeの直後に開始されますが、それ以外の時間は、数秒でとなる場合があります。 (。doInBackgroundが開始されると、パフォーマンスは結構です)android autocomplete AsyncTask delay

セットリスナー:

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の間に遅延がある理由を知っていますか? AsyncTask referenceから

+2

タスクがシリアル化され、前のタスクがキャンセルするのに時間がかかることがあります。 –

+0

これはおそらく聞こえます。あなたはそれを回避する方法について考えていますか? – lf215

答えて

1

タスクは、(boolean)をキャンセル呼び出すことによって、いつでもキャンセルすることができます。このメソッドを呼び出すと、以降のisCancelled()の呼び出しによってtrueが返されます。このメソッドを呼び出すと、doInBackground(Object [])が返された後にonPostExecute(Object)の代わりにonCancelled(Object)が呼び出されます。 は、あなたが常に可能な場合は、([]オブジェクト)doInBackgroundから定期的に)isCancelled(の戻り値をチェックする必要があり、作業はできるだけ迅速にキャンセルされたことを確認するために、(例えばループ内。)

したがって、doInBackgroundでisCancelled()が定期的に設定されているかどうかを手動でチェックしていない限り、実際にdoInBackground()ステップをキャンセルしていません。ほとんどのバージョンのAndroidでは、すべてのAsyncTaskが1つのスレッドを共有しているため、次のタスクを開始する前に終了する必要があります。これがあなたの遅れの理由ですが、doInBackground()コードから十分な情報がない(つまりコードを投稿していない)ので、isCancelled()をチェックする場所を提案する必要があります。

何らかの理由で以前のタスクをキャンセルできない場合は、executeOnExecutor(java.util.concurrent.Executor, Object[])THREAD_POOL_EXECUTORを使用してAsyncTasksを並行して実行することもできますが、これは同じドキュメントが示唆しているとおりですそれはおそらくあなたが今持っているものよりも悪くなるいくつかの不満足なスレッドの問題を引き起こす可能性があります。

+0

"... THREAD_POOL_EXECUTORと同じドキュメントが示唆しているように、あなたがしようとしていることは、ちょっと不愉快なスレッド問題を引き起こすようです。新しい実行の前にキャンセルすると、最新のタスクだけがonPostExecuteを実行すると、スレッドの問題が発生することはありますか?私の投稿にはこれは含まれていませんが、修正された唯一のグローバル状態がonPostExecuteであることを暗示しようとしました: "アダプタによる提案の設定" – lf215

+0

別のスレッドで複数のAsyncTaskを実行すると、最初に開始したものが最初に完了するという保証はありません。だから、遅くても正しい解決策から速くて間違った解決策に進むことができます。もちろん、スレッドプールエグゼキュータを使用することはできますが、準備が難しい問題が発生し、一層厄介なことに、一貫して失敗しません。あなたがデフォルトのエグゼキュータに固執して、doInBackground()のループを使って前のタスクを実際にキャンセルしていることを確認するのはずっと簡単です。 – ajpolt

+0

私は一般的なケースであなたに同意しますが、私が質問に含めたコードを考えると、あなたの評価はあまりにも慎重すぎる/間違っていると思います。 「別のスレッドで複数のAsyncTaskを実行すると、最初に起動したものが最初のものであるという保証はありません。最初のものは気にしません。最後のものは最後にアダプタを最後に変更するだけです。 THREAD_POOL_EXECUTORを使用すると、私の解決策が間違っている可能性がありますが、あなたのコメントにはその証拠は見えません。私の以前の質問に答えてください: "どうすれば... onPostExecuteができますか?" – lf215