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


108

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

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

답변:


76

그냥 발견 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;
    }

    // ...

}

55
running에 대한 부울 플래그를 만드는 대신 제거하고 while (! isCanceled ()) ???
공자

36
onCancelled ()에 대한 문서에서 : " cancel (boolean)이 호출되고 doInBackground (Object [])가 완료된 UI 스레드에서 실행됩니다 ." 이 '이후'는 onCancelled에 플래그를 설정하고 doInBackground를 체크인하는 것이 의미가 없음을 의미합니다.
lopek 2013

2
@confucius 맞습니다.하지만 이렇게하면 백그라운드 스레드가 중단되지 않습니다. 이미지를 업로드 할 때 업로드 프로세스가 백그라운드에서 계속되고 onPostExecute가 호출되지 않았다고 가정합니다.
umesh

1
@DanHulme 나는 공자의 의견이 아니라 답변에 제공된 코드 스 니펫을 참조했다고 생각합니다 (맞습니다).
lopek

4
예,이 답변 은 작동하지 않습니다 . doInBackground에서 교체 while(running)while(!isCancelled())다른 사람이 여기에 코멘트에 말했듯이.
matt5784 2015-08-13

76

계산을하는 경우 :

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

HTTP 요청을하는 경우 :

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

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


감사합니다. 작동하지만이 경우 예외를 피하는 방법은 무엇입니까?
begiPass 2013

HttpGet.abort()백그라운드 스레드에서 호출해야합니다. 그렇지 않으면 android.os.NetworkOnMainThreadException.
Heath Borders

@wrygiel HTTP 요청을하는 경우 요청을 cancel(true)중단 하지 않아야 합니까? 문서에서 :If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Storo

HttpURLConnection.disconnect();
Oded Breiner

AsyncTask에서 CPU를 사용하는 작업이있는 경우 cancel(true). 나는 그것을 사용하고 작동합니다.
SMMousavi 2011

20

문제는 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;
        }
     }

2
이것은 작동하지만 논리적으로 서버 응답을 기다리고 있고 방금 db 작업을 수행했을 때 활동에 대한 올바른 변경 사항을 반영해야합니다. 나는 그것에 대한 블로그를 썼다.
Vikas

4
수락 된 답변에 대한 의견에서 언급했듯이 자체 running플래그 를 만들 필요가 없습니다 . AsyncTask에는 작업이 취소되었을 때 설정되는 내부 플래그가 있습니다. 교체 while (running)와 함께 while (!isCancelled()). developer.android.com/reference/android/os/… 따라서이 간단한 경우에는 onCancelled()재정의 가 필요하지 않습니다 .
ToolmakerSteve 2015-08-26

11

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


일반적인 자바 스레드를 사용하고 휘발성 부울 변수를 사용하여 실행되는 스레드를 "중단"할 것을 제안합니까?
Samuh

34
마이크를 모욕하지는 않지만 수용 가능한 대답은 아닙니다. AsyncTask에는 cancel 메소드가 있으며 작동합니다. 내가 말할 수있는 한, 그렇지 않습니다.하지만 내가 잘못하고 있더라도 작업을 취소하는 올바른 방법이 있어야합니다. 방법은 그렇지 않으면 존재하지 않을 것입니다. 짧은 작업도 취소해야 할 수도 있습니다.로드 즉시 AsyncTask를 시작하는 Activity가 있으며 사용자가 작업을 연 직후에 다시 누르면 작업이 완료 될 때 잠시 후 Force Close가 표시되지만 컨텍스트는 표시되지 않습니다. onPostExecute에서 사용할 수 있도록 존재합니다.
Eric Mill

10
@Klondike : 나는 "Mike"가 누군지 모른다. "하지만 그건 받아 들일 수없는 대답입니다."-귀하의 의견을 환영합니다. "AsyncTask에는 취소 메소드가 있으며 작동합니다." -자바에서 스레드를 취소하는 것은 ~ 15 년 동안 문제였습니다. Android와는 관련이 없습니다. "강제 종료"시나리오와 관련하여 부울 변수로 해결할 수 있습니다.이 변수 onPostExecute()는 작업을 계속해야하는지 여부를 확인하기 위해 테스트 합니다.
CommonsWare

1
@Tejaswi Yerukalapudi : 자동으로 아무것도하지 않을 것입니다. 이 질문에 대한 답변을 참조하십시오.
CommonsWare

10
AsyncTask의 doInBackground에서 isCancelled 메서드를 주기적으로 확인해야합니다. 그것은 바로 문서에 있습니다 : developer.android.com/reference/android/os/…
Christopher Perry

4

이를 수행하는 유일한 방법은 isCancelled () 메서드의 값을 확인하고 true를 반환하면 재생을 중지하는 것입니다.


4

이것이 제가 AsyncTask를 작성하는 방법입니다
. 핵심은 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;
    }

1
비동기 작업에서 단순히 cancel (true)을 호출하고 isCancelled ()를 주기적으로 확인하는 것은 작동하지만 작업이 수행하는 작업에 따라 중단되기까지 최대 60 초가 걸릴 수 있음을 발견했습니다. Thread.sleep (1)을 추가하면 즉시 중단 될 수 있습니다. (비동기 작업은 대기 상태가되며 즉시 삭제되지 않습니다.) 감사합니다.
John J Smith

0

글로벌 AsyncTask 클래스 변수

LongOperation LongOperationOdeme = new LongOperation();

그리고 AsyncTask를 방해하는 KEYCODE_BACK 액션

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

그것은 나를 위해 작동합니다.


0

cancel(true)소켓 또는 파일 스트림 닫기, 로컬 데이터베이스에 데이터 쓰기 등과 같이 해제 할 리소스가있을 수 있기 때문에 불필요하게 비동기 작업을 강제로 중단하고 싶지 않습니다 . 반면에 저는 다음과 같은 상황에 직면했습니다. 비동기 작업은 일부 시간 동안 자체 완료를 거부합니다. 예를 들어, 때때로 주 활동이 닫히고 활동의 onPause()메서드 내에서 완료되도록 비동기 작업을 요청하는 경우가 있습니다 . 따라서 단순히을 호출하는 문제가 아닙니다 running = false. 혼합 솔루션을 찾아야합니다. 둘 다 호출 running = false한 다음 비동기 작업을 완료하는 데 몇 밀리 초를 준 다음 cancel(false)또는 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;
}

결과적으로 doInBackground()완료 후 때때로 onCancelled()메서드가 호출되고 때로는 onPostExecute(). 그러나 적어도 비동기 작업 종료는 보장됩니다.


경쟁 조건처럼 보입니다.
msangel

0

Yanchenko의 답변 인 '10 년 4 월 29 일 : AsyncTask를 실행할 때마다 'doInBackground'아래의 코드를 여러 번 실행해야 할 때 'while (running)'접근 방식을 사용하는 것이 깔끔합니다. 'doInBackground'아래의 코드가 AsyncTask 실행 당 한 번만 실행되어야하는 경우 'while (running)'루프의 'doInBackground'아래에있는 모든 코드를 래핑해도 백그라운드 코드 (백그라운드 스레드)가 실행되는 것을 중지하지 않습니다. AsyncTask 자체는 취소됩니다. 왜냐하면 'while (running)'조건은 while 루프 내의 모든 코드가 적어도 한 번 실행 된 후에 만 ​​평가되기 때문입니다. 따라서 (a.) 'doInBackground'아래의 코드를 여러 'while (running)'블록으로 나누거나 (b.) 수많은 'isCancelled'를 수행해야합니다.https://developer.android.com/reference/android/os/AsyncTask.html .

옵션 (a.)의 경우 Yanchenko의 대답을 다음과 같이 수정할 수 있습니다.

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

// ...

옵션 (b.)의 경우 'doInBackground'의 코드는 다음과 같습니다.

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

// ...
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.