Programming

실행중인 AsyncTask를 취소하는 이상적인 방법

procodes 2020. 8. 7. 21:37
반응형

실행중인 AsyncTask를 취소하는 이상적인 방법


.NET을 사용하여 백그라운드 스레드에서 원격 오디오 파일 가져 오기 및 오디오 파일 재생 작업을 실행하고 AsyncTask있습니다. Cancellable진행 표시 줄은 동작의 실행을 인출 시간이 표시됩니다.

AsyncTask사용자가 작업을 취소 (반대 결정)하면 실행 을 취소 / 중단하고 싶습니다 . 그러한 사건을 처리하는 이상적인 방법은 무엇입니까?


그냥 발견 AlertDialogsboolean cancel(...);나는 모든 곳에서 실제로 사용 아무것도 안 했어요. 큰.
그래서...

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;
    }

    // ...

}

계산을하는 경우 :

  • isCancelled()주기적으로 확인 해야합니다.

HTTP 요청을하는 경우 :

  • 자신 HttpGet또는 HttpPost다른 곳에 인스턴스를 저장합니다 (예 : 공개 필드).
  • 전화 한 후을 호출 cancel하십시오 request.abort(). 이것은 IOException당신의 doInBackground.

제 경우에는 다양한 AsyncTasks에서 사용한 커넥터 클래스가 있습니다. 간단하게 abortAllRequests하기 위해 해당 클래스에 메서드를 추가 하고을 호출 한 직후이 메서드를 호출했습니다 cancel.


문제는 AsyncTask.cancel () 호출이 작업에서 onCancel 함수 만 호출한다는 것입니다. 여기에서 취소 요청을 처리 할 수 ​​있습니다.

다음은 업데이트 메서드를 트리거하는 데 사용하는 작은 작업입니다.

private class UpdateTask extends AsyncTask<Void, Void, Void> {

        private boolean running = true;

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

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
            onUpdate();
        }

        @Override
        protected Void doInBackground(Void... params) {
             while(running) {
                 publishProgress();
             }
             return null;
        }
     }

간단합니다 AsyncTask.. AsyncTask빠르게 (수십 초) 종료되므로 취소 할 필요가없는 짧은 작업을 위해 설계되었습니다. "오디오 파일 재생"은 적합하지 않습니다. 일반 오디오 파일 재생을 위해 백그라운드 스레드가 필요하지 않습니다.


The only way to do it is by checking the value of the isCancelled() method and stopping playback when it returns true.


This is how I write my AsyncTask
the key point is add Thread.sleep(1);

@Override   protected Integer doInBackground(String... params) {

        Log.d(TAG, PRE + "url:" + params[0]);
        Log.d(TAG, PRE + "file name:" + params[1]);
        downloadPath = params[1];

        int returnCode = SUCCESS;
        FileOutputStream fos = null;
        try {
            URL url = new URL(params[0]);
            File file = new File(params[1]);
            fos = new FileOutputStream(file);

            long startTime = System.currentTimeMillis();
            URLConnection ucon = url.openConnection();
            InputStream is = ucon.getInputStream();
            BufferedInputStream bis = new BufferedInputStream(is);

            byte[] data = new byte[10240]; 
            int nFinishSize = 0;
            while( bis.read(data, 0, 10240) != -1){
                fos.write(data, 0, 10240);
                nFinishSize += 10240;
                **Thread.sleep( 1 ); // this make cancel method work**
                this.publishProgress(nFinishSize);
            }              
            data = null;    
            Log.d(TAG, "download ready in"
                  + ((System.currentTimeMillis() - startTime) / 1000)
                  + " sec");

        } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                returnCode = FAIL;
        } catch (Exception e){
                 e.printStackTrace();           
        } finally{
            try {
                if(fos != null)
                    fos.close();
            } catch (IOException e) {
                Log.d(TAG, PRE + "Error: " + e);
                e.printStackTrace();
            }
        }

        return returnCode;
    }

Our global AsyncTask class variable

LongOperation LongOperationOdeme = new LongOperation();

And KEYCODE_BACK action which interrupt AsyncTask

   @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            LongOperationOdeme.cancel(true);
        }
        return super.onKeyDown(keyCode, event);
    }

It works for me.


I don't like to force interrupt my async tasks with cancel(true) unnecessarily because they may have resources to be freed, such as closing sockets or file streams, writing data to the local database etc. On the other hand, I have faced situations in which the async task refuses to finish itself part of the time, for example sometimes when the main activity is being closed and I request the async task to finish from inside the activity's onPause() method. So it's not a matter of simply calling running = false. I have to go for a mixed solution: both call running = false, then giving the async task a few milliseconds to finish, and then call either cancel(false) or cancel(true).

if (backgroundTask != null) {
    backgroundTask.requestTermination();
    try {
        Thread.sleep((int)(0.5 * 1000));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    if (backgroundTask.getStatus() != AsyncTask.Status.FINISHED) {
        backgroundTask.cancel(false);
    }
    backgroundTask = null;
}

As a side result, after doInBackground() finishes, sometimes the onCancelled() method is called, and sometimes onPostExecute(). But at least the async task termination is guaranteed.


With reference to Yanchenko's answer on 29 April '10: Using a 'while(running)' approach is neat when your code under 'doInBackground' has to be executed multiple times during every execution of the AsyncTask. If your code under 'doInBackground' has to be executed only once per execution of the AsyncTask, wrapping all your code under 'doInBackground' in a 'while(running)' loop will not stop the background code (background thread) from running when the AsyncTask itself is cancelled, because the 'while(running)' condition will only be evaluated once all the code inside the while loop has been executed at least once. You should thus either (a.) break up your code under 'doInBackground' into multiple 'while(running)' blocks or (b.) perform numerous 'isCancelled' checks throughout your 'doInBackground' code, as explained under "Cancelling a task" at https://developer.android.com/reference/android/os/AsyncTask.html.

For option (a.) one can thus modify Yanchenko's answer as follows:

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

private volatile boolean running = true;

//...

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

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

    // does the hard work

    while (running) {
        // part 1 of the hard work
    }

    while (running) {
        // part 2 of the hard work
    }

    // ...

    while (running) {
        // part x of the hard work
    }
    return null;
}

// ...

For option (b.) your code in 'doInBackground' will look something like this:

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

//...

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

    // part 1 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // part 2 of the hard work
    // ...
    if (isCancelled()) {return null;}

    // ...

    // part x of the hard work
    // ...
    if (isCancelled()) {return null;}
}

// ...

참고URL : https://stackoverflow.com/questions/2735102/ideal-way-to-cancel-an-executing-asynctask

반응형