Nice programing

Android : 비동기 작업 취소

nicepro 2020. 12. 26. 16:35
반응형

Android : 비동기 작업 취소


비동기 작업을 사용하여 이미지를 업로드하고 결과를 얻습니다.

이미지를 업로드하는 동안 다음과 같이 onPreExecute () 메서드로 작성된 진행률 대화 상자가 표시됩니다.

    protected void onPreExecute() { 
         uploadingDialog = new ProgressDialog(MyActivity.this); 
         uploadingDialog.setMessage("uploading"); 
         uploadingDialog.setCancelable(true);
         uploadingDialog.show();
    }

좋아, 뒤로 버튼을 누르면 분명히 setCancelable (true) 때문에 대화 상자가 사라집니다.

그러나 (분명히) 비동기 작업은 멈추지 않습니다.

그래서 어떻게 고칠 수 있습니까? 뒤로 버튼을 눌렀을 때 대화 상자와 비동기 작업을 모두 취소하고 싶습니다. 어떤 아이디어?

편집 : 해결책을 찾았습니다 . 아래 내 답변을 참조하십시오.


SDK에서 :

작업 취소

cancel (boolean)을 호출하여 언제든지 작업을 취소 할 수 있습니다. 이 메서드를 호출하면 isCancelled ()에 대한 후속 호출이 true를 반환합니다.

이 메서드를 호출하면 doInBackground (Object [])가 반환 된 후 onPostExecute (Object) 대신 onCancelled (Object)가 호출됩니다.

작업이 가능한 한 빨리 취소되도록하려면 가능한 경우 (예를 들어 루프 내부) doInBackground (Object [])에서 주기적으로 isCancelled ()의 반환 값을 항상 확인해야합니다.

따라서 귀하의 코드는 대화 리스너에 적합합니다.

uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
    public void onCancel(DialogInterface dialog) {
        myTask.cancel(true);
        //finish();
    }
});

이제 앞서 SDK에서 언급했듯이 작업이 취소되었는지 여부를 확인해야합니다 . onPreExecute () 메서드 내에서 isCancelled () 를 확인해야하기 때문 입니다.

예를 들면 :

if (isCancelled()) 
    break;
else
{
   // do your work here
}

해결책을 찾았습니다 : 다음과 같이 uploadingDialog.show () 전에 액션 리스너를 추가했습니다.

    uploadingDialog.setOnCancelListener(new DialogInterface.OnCancelListener(){
          public void onCancel(DialogInterface dialog) {
              myTask.cancel(true);
              //finish();
          }
    });

그런 식으로 뒤로 버튼을 누르면 위의 OnCancelListener가 대화 상자와 작업을 모두 취소합니다. 또한 뒤로 누를 때 전체 활동을 완료하려면 finish ()를 추가 할 수 있습니다. 비동기 작업을 다음과 같은 변수로 선언해야합니다.

    MyAsyncTask myTask=null;

다음과 같이 비동기 작업을 실행하십시오.

    myTask = new MyAsyncTask();
    myTask.execute();

나는 이것을 알아내는 데 시간을 보냈다. 내가 원했던 것은 그것을 수행하는 방법에 대한 간단한 예 였기 때문에 내가 어떻게했는지 게시 할 것이라고 생각했다. 다음은 라이브러리를 업데이트하고 사용자가 대화 상자를 닫으면 얼마나 많은 책이 업데이트되고 취소되는지 보여주는 진행 대화 상자가있는 코드입니다.

private class UpdateLibrary extends AsyncTask<Void, Integer, Boolean>{
    private ProgressDialog dialog = new ProgressDialog(Library.this);
    private int total = Library.instance.appState.getAvailableText().length;
    private int count = 0;

    //Used as handler to cancel task if back button is pressed
    private AsyncTask<Void, Integer, Boolean> updateTask = null;

    @Override
    protected void onPreExecute(){
        updateTask = this;
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        dialog.setOnDismissListener(new OnDismissListener() {               
            @Override
            public void onDismiss(DialogInterface dialog) {
                updateTask.cancel(true);
            }
        });
        dialog.setMessage("Updating Library...");
        dialog.setMax(total);
        dialog.show();
    }

    @Override
    protected Boolean doInBackground(Void... arg0) {
            for (int i = 0; i < appState.getAvailableText().length;i++){
                if(isCancelled()){
                    break;
                }
                //Do your updating stuff here
            }
        }

    @Override
    protected void onProgressUpdate(Integer... progress){
        count += progress[0];
        dialog.setProgress(count);
    }

    @Override
    protected void onPostExecute(Boolean finished){
        dialog.dismiss();
        if (finished)
            DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY, Str.TEXT_UPDATECOMPLETED, Library.instance);
        else 
            DialogHelper.showMessage(Str.TEXT_UPDATELIBRARY,Str.TEXT_NOUPDATE , Library.instance);
    }
}

활동에 다음과 같은 멤버 변수를 만듭니다.

YourAsyncTask mTask;
Dialog mDialog;

대화 및 작업에 사용하십시오.

onPause ()에서 간단히

if(mTask!=null) mTask.cancel(); 
if(mDialog!=null) mDialog.dismiss();

코드를 개선하고 싶습니다. 당신이 CANEL 때 (의 콜백 메서드를 ) 자동으로 호출됩니다, 당신은 당신이 숨길 수 있습니다 .aSyncTaskonCancelled()aSyncTaskprogressBarDialog

이 코드도 포함 할 수 있습니다.

public class information extends AsyncTask<String, String, String>
    {
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(String... arg0) {
            return null;
        }

        @Override
        protected void onPostExecute(String result) {
            super.onPostExecute(result);
            this.cancel(true);
        }

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

        @Override
        protected void onCancelled() {
            Toast.makeText(getApplicationContext(), "asynctack cancelled.....", Toast.LENGTH_SHORT).show();
            dialog.hide(); /*hide the progressbar dialog here...*/
            super.onCancelled();
        }

    }

AsyncTask를 사용하는 대부분의 경우 비즈니스 로직은 UI가 아닌 별도의 비즈니스 클래스에 있습니다. 이 경우 doInBackground ()에서 루프를 가질 수 없습니다. 예를 들어 서비스를 사용하고 데이터를 차례로 유지하는 동기화 프로세스가 있습니다.

결국 취소를 처리 할 수 ​​있도록 업무를 비즈니스 객체에 넘깁니다. 내 설정은 다음과 같습니다.

public abstract class MyActivity extends Activity {

    private Task mTask;
    private Business mBusiness;

    public void startTask() {
        if (mTask != null) {
            mTask.cancel(true);
        }
        mTask = new mTask();
        mTask.execute();
    }
}

protected class Task extends AsyncTask<Void, Void, Boolean> {
    @Override
    protected void onCancelled() {
        super.onCancelled();

        mTask.cancel(true);

        // ask if user wants to try again
    }

    @Override
    protected Boolean doInBackground(Void... params) {
        return mBusiness.synchronize(this);
    }

    @Override
    protected void onPostExecute(Boolean result) {
        super.onPostExecute(result);

        mTask = null;

        if (result) {
            // done!
        }
        else {
            // ask if user wants to try again
        }
    }
}

public class Business {
    public boolean synchronize(AsyncTask<?, ?, ?> task) {
        boolean response = false;
        response = loadStuff(task);

        if (response)
            response = loadMoreStuff(task);

        return response;
    }

    private boolean loadStuff(AsyncTask<?, ?, ?> task) {
        if (task != null && task.isCancelled()) return false;

        // load stuff

        return true;
    }
}

취소를 요청할 수 있지만 실제로 종료 할 수는 없습니다. 답변을 참조하십시오 .


I had a similar problem - essentially I was getting a NPE in an async task after the user had destroyed the activity. After researching the problem on Stack Overflow, I adopted the following solution:

volatile boolean running;

public void onActivityCreated (Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    running=true;
    ...
    }


public void onDestroy() {
    super.onDestroy();

    running=false;
    ...
}

Then, I check "if running" periodically in my async code. I have stress tested this and I am now unable to "break" my activity. This works perfectly and has the advantage of being simpler than some of the solutions I have seen on SO.


How to cancel AsyncTask

Full answer is here - Android AsyncTask Example

AsyncTask provides a better cancellation strategy, to terminate currently running task.

cancel(boolean mayInterruptIfitRunning)

myTask.cancel(false)- It makes isCancelled returns true. Helps to cancel the task.

myTask.cancel(true) – It also makes isCancelled() returns true, interrupt the background thread and relieves resources .

It is considered as an arrogant way, If there is any thread.sleep() method performing in background thread, cancel(true) will interrupt background thread at that time. But cancel(false) will wait for it and cancel task when that method completes.

If you invoke cancel() and doInBackground() hasn’t begun execute yet. onCancelled() will invoke.

After invoking cancel(…) you should check value returned by isCancelled() on doInbackground() periodically. just like shown below.

protected Object doInBackground(Params… params)  { 
 while (condition)
{
 ...
if (isCancelled()) 
break;
}
return null; 
}

ReferenceURL : https://stackoverflow.com/questions/6039158/android-cancel-async-task

반응형