AsyncTask를 여러 번 실행


127

내 활동에서는 AsyncTask에서 확장되는 클래스와 해당 AsyncTask의 인스턴스 인 매개 변수를 사용합니다. 내가 전화하면 mInstanceOfAT.execute("")모든 것이 괜찮습니다. 그러나 AsyncTask를 다시 호출하는 업데이트 버튼을 누르면 앱이 충돌합니다 (네트워크 작업이 작동하지 않은 경우). 그런 다음 원인은 예외를 나타냅니다

작업을 실행할 수 없음 : 작업이 이미 실행되었습니다 (작업은 한 번만 실행할 수 있음)

Asyctask 인스턴스에 대해 cancel (true)을 호출하려고 시도했지만 작동하지 않습니다. 지금까지 유일한 솔루션은 Asyntask의 새 인스턴스를 작성하는 것입니다. 이것이 올바른 방법입니까?

감사.

답변:


217

AsyncTask 인스턴스는 한 번만 사용할 수 있습니다.

대신, 다음과 같이 작업을 호출하십시오. new MyAsyncTask().execute("");

AsyncTask API 문서에서 :

스레딩 규칙

이 클래스가 제대로 작동하려면 몇 가지 스레딩 규칙을 따라야합니다.

  • 작업 인스턴스는 UI 스레드에서 작성해야합니다.
  • UI 스레드에서 execute (Params ...)를 호출해야합니다.
  • onPreExecute (), onPostExecute (Result), doInBackground (Params ...), onProgressUpdate (Progress ...)를 수동으로 호출하지 마십시오.
  • 작업은 한 번만 실행할 수 있습니다 (두 번째 실행을 시도하면 예외가 발생 함).

2
내가 한 말은 유일한 가능성일까요? 원인 새 개체를 만드는 대신 메모리를 저장하고 싶습니다.
Dayerman


@StevePrentice : 새로운 task (). execute (param)로 x 초마다 작업 인스턴스를 생성하면 서버에 데이터를 보내려면 실행이 완료되면 가비지 수집기가 어떻게 메모리를 해제 할 수 있습니까?
Ant4res

3
@ Ant4res, 비동기 작업 인스턴스를 참조하지 않는 한 GC는 메모리를 해제합니다. 그러나 진행중인 백그라운드 작업이있는 경우 doInBackground 내부에서 루프로 작업을 수행하고 publishProgress를 호출하여 진행 상황을 업데이트 할 수 있습니다. 또는 다른 방법은 작업을 백그라운드 스레드에 넣는 것입니다. 여기에는 다양한 접근법이 있지만 세부 사항이 없으면 서로를 추천 할 수 없습니다.
Steve Prentice

28

Steve Prentice의 답변에 ASyncTask 인스턴스가 발생하는 이유가 자세히 설명되어 있습니다. 그러나 ASyncTask를 실행하는 횟수 가 제한되어 있지만 스레드가 실행되는 동안 원하는 작업을 자유롭게 수행 할 수 있습니다. .

실행 코드를 doInBackground () 내의 루프 안에 넣고 동시 잠금을 사용하여 각 실행을 트리거하십시오. publishProgress () / onProgressUpdate ()를 사용하여 결과를 검색 할 수 있습니다 .

예:

class GetDataFromServerTask extends AsyncTask<Input, Result, Void> {

    private final ReentrantLock lock = new ReentrantLock();
    private final Condition tryAgain = lock.newCondition();
    private volatile boolean finished = false;

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

        lock.lockInterruptibly();

        do { 
            // This is the bulk of our task, request the data, and put in "result"
            Result result = ....

            // Return it to the activity thread using publishProgress()
            publishProgress(result);

            // At the end, we acquire a lock that will delay
            // the next execution until runAgain() is called..
            tryAgain.await();

        } while(!finished);

        lock.unlock();
    }

    @Override
    protected void onProgressUpdate(Result... result) 
    {
        // Treat this like onPostExecute(), do something with result

        // This is an example...
        if (result != whatWeWant && userWantsToTryAgain()) {
            runAgain();
        }
    }

    public void runAgain() {
        // Call this to request data from the server again
        tryAgain.signal();
    }

    public void terminateTask() {
        // The task will only finish when we call this method
        finished = true;
        lock.unlock();
    }

    @Override
    protected void onCancelled() {
        // Make sure we clean up if the task is killed
        terminateTask();
    }
}

물론 이것은 기존의 ASyncTask 사용법보다 약간 더 복잡 하며 실제 진행률보고 에 publishProgress () 사용을 포기합니다 . 그러나 메모리가 중요한 경우이 방법을 사용하면 런타임시 하나의 ASyncTask 만 힙에 남아있게됩니다.


그러나 요점은 실행중인 동안 Asyntask를 다시 실행하고 싶지 않지만이 데이터가 완료되어 데이터를 수신하지 못했기 때문에 다시 호출한다는 것입니다.
Dayerman 2016 년

실제로이 방법으로 ASyncTask를 한 번만 실행하면 onPublishProgress 메서드 내에서 데이터가 올바른지 확인할 수 있습니다 (또는 다른 곳에서 검사를 위임 할 수 있음). 나는 비슷한 패턴으로 잠시 동안이 패턴을 사용했다 (많은 작업이 빠르게 연속적으로 발생하여 힙 크기를 위험에 빠뜨린다).
seanhodges

그러나 그 순간 서버가 응답하지 않고 10 초 후에 다시 시도하고 싶다면 어떻게해야합니까? AsyncTask는 이미 끝났습니다. 그런 다음 다시 호출해야합니다
Dayerman

의미하는 바를 설명하기 위해 예제 코드를 추가했습니다. ASyncTask는 결과에 만족하고 "terminateTask ()"를 호출 한 후에 만 ​​완료됩니다.
seanhodges

1
당신이 얻을 경우 IllegalMonitorStateExceptionrunAgain(호출 onProgressUpdate:이 답변 참조) stackoverflow.com/a/42646476/2711811을 . 그것은 / signal()로 둘러 싸일 필요가 있음을 제안합니다 (그리고 나를 위해 일했습니다) . 이것은 호출 타이밍과 관련이있을 수 있습니다 . lockunlockpublishProgressonProgressUpdate
Andy

2

나는 같은 문제가 있었다. 내 경우에는 내가하고 싶은 작업 onCreate()이 있습니다 onResume(). 그래서 Asynctask를 정적으로 만들고 인스턴스를 가져옵니다. 이제도 여전히 같은 문제가 있습니다.

그래서 onPostExecute ()에서 한 일은 다음과 같습니다.

instance = null;

정적 getInstance 메소드에서 내 인스턴스가 null이 아닌지 확인하고 그렇지 않으면 인스턴스를 작성합니다.

if (instance == null){
    instance = new Task();
}
return instance;

postExecute의 메소드는 인스턴스를 비우고 다시 작성합니다. 물론 이것은 수업 밖에서 할 수 있습니다.


1

회전 작업을 정적으로 설정 한 다음 회전 변경시 ​​UI 스레드에 연결, 분리 및 다시 연결하는 데 도움이되었습니다. 그러나 귀하의 질문으로 돌아가려면 스레드가 실행 중인지 확인하는 플래그를 작성하십시오. 스레드를 다시 시작하고 싶을 때 회전 작업이 경고인지 경고인지 확인합니다. 그렇지 않은 경우 null로 만든 다음 새 오류를 생성하면 현재보고있는 오류를 해결할 수 있습니다. 또한 성공적으로 완료되면 완료된 회전 인식 작업을 무효화하여 다시 시작할 수 있습니다.


0

그렇습니다, 의사는 하나의 Asyntask 만 실행할 수 있다고 말합니다.

당신이 그것을 사용해야 할 때마다 당신은 인스턴스해야합니다 :

// Any time if you need to call her
final FirmwareDownload fDownload = new FirmwareDownload();
fDownload.execute("your parameter");

static class FirmwareDownload extends AsyncTask<String, String, String> {
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.