15

これは重複した質問である可能性がありますが、探しているものが見つかりませんでした。UI アクティビティ new LoadData().execute();で AsyncTask を呼び出しており、doInBackground で時間がかかるメソッドを呼び出しています。しばらくしてデータが返されない場合は、このスレッドを中断したいと思います。以下は、私がこれをやろうとしたコードです。

class LoadData extends AsyncTask<String, String, String>
{
    @Override
    protected void onPreExecute() {
    super.onPreExecute();
    startTime = System.currentTimeMillis();
    }
    protected String doInBackground(String... args)
    {

        DataCollector dc = new DataCollector();
        data = dc.collectData(query);
        //Here I check if the time is greater than 30 seconds then cancel
        if(((System.currentTimeMillis()-startTime)/1000)>30)
        {
           cancel(true);
        }
    return null;
    }
}

しかし、これは 30 秒経ってもタスクを停止せず、実際にはもっと時間がかかっています。私も試してみget(long timeout, TimeUnit unit);ましたが、それもうまくいきません。

どうすればいいのか、またはdoInBackgroundでisCancelled()を使用する方法を教えてもらえますか。

ありがとう。

4

5 に答える 5

3

別のスレッドで時間チェックを行う必要があります。

現在行っていることは、(バックグラウンドで) を実行し、dc.collectData(query)準備ができたら、キャンセルする必要があるかどうかを確認することです。そのため、クエリに 1 分かかる場合は、1 分後にキャンセル チェックを行いますが、これでは既に遅すぎます。

あなたができることは、LoadData().execute() の 30 秒後に実行する TimerTask をスケジュールすることです。タイマー Task が実行されている場合は、AsyncTask をキャンセルできます (まだ実行中の場合)。

于 2013-06-26T08:44:22.513 に答える
2

これを非同期/待機の問題に変換して、すべての高価なメソッドを非同期メソッドとして作成します。

まず、DataCollector の collectData(query) を collectDataAsync(query) に変更します。(DataCollector を変更できない場合は、ラムダ関数などでラップする回避策があります)。

次に、次のように doInBackground を非同期タスクとして変更します。

protected async Task<String> doInBackgroundAsync(String... args)
{
    DataCollector dc = new DataCollector();
    int timeout = 1000;
    var task = dc.collectDataAsync(query);
    if (await Task.WhenAny(task, Task.Delay(timeout)) == task) {
        // task completed within timeout
        data = task.Result;
    } else { 
        // timeout logic
    }
}

基本的に、doInBackgroundAsync 内には、collectDataAsync と delay タスクの 2 つのタスクがあります。あなたのコードはより速いものを待ちます。そうすれば、どれがどれだったかがわかるので、それに応じて反応できます。

collectDataAsync タスクもキャンセルする必要がある場合は、 cancelToken を使用します。これを使用して問題を解決しますhttps://stackoverflow.com/a/11191070/3307066

現在 doInBackgroundAsync は非同期であるため、使用方法が少し変更されていることに注意してください。

それが役に立てば幸い。

于 2014-11-09T02:09:26.563 に答える
1

簡単な答えは、AsyncTask が開始されるとキャンセルできないということです。あなたができることは、内部doInBackGround()にループを挿入することです。これは、将来いつか true に設定されているかどうかをチェックしisCancelled()、関数から値を返します (onPostExecute()定義した場合は、それが呼び出されます)。

AsyncTask を停止できないからといって、メモリが不足している場合に OS がそれをキャンセルしないわけではないことに注意してください。AsyncTask で重要なタスク (100% 実行したいタスク) を実行している場合は、このことを念頭に置いておく必要があります。その場合は、必要に応じて OS によって自動的に強制終了および再起動されるコンポーネントであるServiceを使用することをお勧めします。

于 2013-06-26T08:49:36.130 に答える
0

これを試して :

public class MyTask extends AsyncTask<Void, Void, Void> {

    private volatile boolean running = true;
    private final ProgressDialog progressDialog;

    public MyTask(Context ctx) {
        progressDialog = gimmeOne(ctx);

        progressDialog.setCancelable(true);
        progressDialog.setOnCancelListener(new OnCancelListener() {
            @Override
            public void onCancel(DialogInterface dialog) {
                // actually could set running = false; right here, but I'll
                // stick to contract.
                cancel(true);
            }
        });

    }

    @Override
    protected void onPreExecute() {
        progressDialog.show();
    }

    @Override
    protected void onCancelled() {
        running = false;
    }

    @Override
    protected Void doInBackground(Void... params) {

        while (running) {
            // does the hard work
        }
        return null;
    }

    // ...

}

礼儀と詳細については、この回答を参照してください。

于 2013-06-26T08:46:00.600 に答える