Android에서 AsyncTask 및 오류 처리


147

내가 사용하는 내 코드를 변환하고 있습니다 HandlerAsyncTask. 후자는 기본 UI 스레드에서 비동기 업데이트 및 결과 처리와 같은 기능을 수행합니다. 나에게 분명하지 않은 것은 무언가가 haywire로 들어가면 예외를 처리하는 방법입니다 AsyncTask#doInBackground.

내가하는 방법은 오류 처리기가 있고 메시지를 보내는 것입니다. 그것은 잘 작동하지만 "올바른"접근법입니까 아니면 더 나은 대안이 있습니까?

또한 오류 처리기를 활동 필드로 정의하면 UI 스레드에서 실행되어야한다는 것을 이해합니다. 그러나 때로는 (매우 예기치 않게) 트리거 된 코드 Handler#handleMessage가 잘못된 스레드 에서 실행되고 있다는 예외 가 발생 합니다. Activity#onCreate대신 오류 처리기를 초기화해야합니까 ? 배치 는 중복 runOnUiThreadHandler#handleMessage것처럼 보이지만 매우 안정적으로 실행됩니다.


왜 코드를 변환하고 싶습니까? 좋은 이유가 있었습니까?
HGPB

4
@Haraldo 그것은 적어도 더 나은 코딩 연습입니다. 이것이 제가 느끼는 방식입니다
Bostone

답변:


178

그것은 잘 작동하지만 "올바른"접근법이며 더 나은 대안이 있습니까?

나는 인스턴스 자체 Throwable또는 인스턴스를 잡고 Exception에서 AsyncTask무언가를 수행 onPostExecute()하므로 오류 처리에는 화면에 대화 상자를 표시하는 옵션이 있습니다.


8
훌륭한! 더 이상 핸들러를 가지고 원숭이 필요 없음
Bostone

5
이것이 Throwable 또는 Exception을 잡아야하는 방법입니까? "백그라운드 처리 결과를 보유 할 자체 AsyncTask 서브 클래스에 인스턴스 변수를 추가하십시오." 예외가 발생하면이 변수에 예외 (또는 다른 오류 문자열 / 코드)를 저장하십시오. onPostExecute가 호출 될 때이 인스턴스 변수가 오류로 설정되어 있는지 확인하십시오. 그렇다면 오류 메시지를 표시하십시오. "("Streets of Boston "사용자 그룹 groups.google.com/group/android-developers/browse_thread/thread/… )
OneWorld

1
@OneWorld : 그렇습니다.
CommonsWare

2
안녕 CW, 당신은 이것을 좀 더 자세하게 설명해 줄 수 있습니까-아마도 간단한 코드 예제로? 고마워요 !!
Bruiser

18
@Bruiser : github.com/commonsguy/cw-lunchlist/tree/master/15-Internet/…AsyncTask 내가 설명하는 패턴 은 다음과 같습니다.
CommonsWare

140

AsyncResult 개체를 만듭니다 (다른 프로젝트에서도 사용할 수 있음).

public class AsyncTaskResult<T> {
    private T result;
    private Exception error;

    public T getResult() {
        return result;
    }

    public Exception getError() {
        return error;
    }

    public AsyncTaskResult(T result) {
        super();
        this.result = result;
    }

    public AsyncTaskResult(Exception error) {
        super();
        this.error = error;
    }
}

AsyncTask doInBackground 메소드에서이 오브젝트를 리턴하고 postExecute에서 점검하십시오. (이 클래스를 다른 비동기 작업의 기본 클래스로 사용할 수 있습니다)

아래는 웹 서버에서 JSON 응답을 얻는 작업 모형입니다.

AsyncTask<Object,String,AsyncTaskResult<JSONObject>> jsonLoader = new AsyncTask<Object, String, AsyncTaskResult<JSONObject>>() {

        @Override
        protected AsyncTaskResult<JSONObject> doInBackground(
                Object... params) {
            try {
                // get your JSONObject from the server
                return new AsyncTaskResult<JSONObject>(your json object);
            } catch ( Exception anyError) {
                return new AsyncTaskResult<JSONObject>(anyError);
            }
        }

        protected void onPostExecute(AsyncTaskResult<JSONObject> result) {
            if ( result.getError() != null ) {
                // error handling here
            }  else if ( isCancelled()) {
                // cancel handling here
            } else {

                JSONObject realResult = result.getResult();
                // result handling here
            }
        };

    }

1
나는 그것을 좋아한다. 좋은 캡슐화. 이것은 원래의 답변의 역설이기 때문에 대답은 유지되지만 이것은 분명히 가치가 있습니다
Bostone

이것은 Generics가 얼마나 유용한 지에 대한 아주 좋은 데모입니다. 복잡성 측면에서 이상한 냄새가 나지만 실제로 표현할 수는 없습니다.
num1

4
좋은 생각, 하나의 질문 : 당신은 왜 부르지 super()에서 AsyncTaskResult클래스가 아무것도를 확장하지 않을 때?
donturner

7
"무해"-중복 코드는 항상 가독성 및 유지 관리에 해 롭습니다. 거기서 꺼내! :)
donturner

2
솔루션이 정말 마음에 들었습니다. 그것에 대해 생각하게 될 것입니다. C #
Vova

11

예외를 AsyncTask올바르게 처리해야한다고 생각하면 이것을 슈퍼 클래스로 사용합니다.

public abstract class ExceptionAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {

    private Exception exception=null;
    private Params[] params;

    @Override
    final protected Result doInBackground(Params... params) {
        try {
            this.params = params; 
            return doInBackground();
        }
        catch (Exception e) {
            exception = e;
            return null;
        }
    }

    abstract protected Result doInBackground() throws Exception;

    @Override
    final protected void onPostExecute(Result result) {
        super.onPostExecute(result);
        onPostExecute(exception, result);
    }

    abstract protected void onPostExecute(Exception exception, Result result);

    public Params[] getParams() {
        return params;
    }

}

일반적으로 doInBackground백그라운드 작업을 수행하기 위해 서브 클래스에서 재정 의하여 필요한 경우 예외를 행복하게 처리합니다. 그런 다음 onPostExecute추상적이기 때문에 구현해야 Exception하며 매개 변수로 전달되는 모든 유형을 처리하도록 부드럽게 상기시킵니다 . 대부분의 경우 예외는 일부 유형의 UI 출력으로 이어 지므로 onPostExecute이를 수행하기에 완벽한 장소입니다.


1
우와, 왜 params앞으로 나아 가지 않습니까? 그래서 원본과 비슷하고 마이그레이션하기가 더 쉽습니까?
TWiStErRob

@TWiStErRob 그 아이디어에는 아무런 문제가 없습니다. 필자는 매개 변수를 사용하지 않는 경향이 있기 때문에 개인적 취향의 문제입니다. 나는 new Task("Param").execute()이상을 선호 합니다 new Task().execute("Param").
sulai

5

다른 이점을 제공하는 RoboGuice 프레임 워크를 사용하려면 추가 콜백 onException ()이있는 RoboAsyncTask를 사용해보십시오. 잘 작동하고 사용합니다. http://code.google.com/p/roboguice/wiki/RoboAsyncTask


이것에 대한 당신의 경험은 무엇입니까? 꽤 안정적입니까?
nickaknudson 2013

RoboGuice아직 살아? 2012 년 이후로 업데이트되지 않은 것 같습니다.
Dimitry K

아니요, RoboGuice는 죽었고 더 이상 사용되지 않습니다. Dagger2가 권장되는 대체품이지만 베어 본 DI 라이브러리입니다.
Avi Cherry

3

성공과 실패에 대한 콜백을 정의하는 인터페이스를 사용하여 자체 AsyncTask 하위 클래스를 만들었습니다. 따라서 AsyncTask에서 예외가 발생하면 onFailure 함수가 예외를 전달하고 그렇지 않으면 onSuccess 콜백이 결과를 전달합니다. 안드로이드에 더 나은 무언가가없는 이유는 저 밖에 있습니다.

public class SafeAsyncTask<inBackgroundType, progressType, resultType>
extends AsyncTask<inBackgroundType, progressType, resultType>  {
    protected Exception cancelledForEx = null;
    protected SafeAsyncTaskInterface callbackInterface;

    public interface SafeAsyncTaskInterface <cbInBackgroundType, cbResultType> {
        public Object backgroundTask(cbInBackgroundType[] params) throws Exception;
        public void onCancel(cbResultType result);
        public void onFailure(Exception ex);
        public void onSuccess(cbResultType result);
    }

    @Override
    protected void onPreExecute() {
        this.callbackInterface = (SafeAsyncTaskInterface) this;
    }

    @Override
    protected resultType doInBackground(inBackgroundType... params) {
        try {
            return (resultType) this.callbackInterface.backgroundTask(params);
        } catch (Exception ex) {
            this.cancelledForEx = ex;
            this.cancel(false);
            return null;
        }
    }

    @Override
    protected void onCancelled(resultType result) {
        if(this.cancelledForEx != null) {
            this.callbackInterface.onFailure(this.cancelledForEx);
        } else {
            this.callbackInterface.onCancel(result);
        }
    }

    @Override
    protected void onPostExecute(resultType result) {
        this.callbackInterface.onSuccess(result);
    }
}

3

Cagatay Kalan 솔루션에 대한보다 포괄적 인 솔루션 은 다음과 같습니다.

AsyncTaskResult

public class AsyncTaskResult<T> 
{
    private T result;
    private Exception error;

    public T getResult() 
    {
        return result;
    }

    public Exception getError() 
    {
        return error;
    }

    public AsyncTaskResult(T result) 
    {
        super();
        this.result = result;
    }

    public AsyncTaskResult(Exception error) {
        super();
        this.error = error;
    }
}

ExceptionHandlingAsyncTask

public abstract class ExceptionHandlingAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, AsyncTaskResult<Result>>
{
    private Context context;

    public ExceptionHandlingAsyncTask(Context context)
    {
        this.context = context;
    }

    public Context getContext()
    {
        return context;
    }

    @Override
    protected AsyncTaskResult<Result> doInBackground(Params... params)
    {
        try
        {
            return new AsyncTaskResult<Result>(doInBackground2(params));
        }
        catch (Exception e)
        {
            return new AsyncTaskResult<Result>(e);
        }
    }

    @Override
    protected void onPostExecute(AsyncTaskResult<Result> result)
    {
        if (result.getError() != null)
        {
            onPostException(result.getError());
        }
        else
        {
            onPostExecute2(result.getResult());
        }
        super.onPostExecute(result);
    }

    protected abstract Result doInBackground2(Params... params);

    protected abstract void onPostExecute2(Result result);

    protected void onPostException(Exception exception)
    {
                        new AlertDialog.Builder(context).setTitle(R.string.dialog_title_generic_error).setMessage(exception.getMessage())
                .setIcon(android.R.drawable.ic_dialog_alert).setPositiveButton(R.string.alert_dialog_ok, new DialogInterface.OnClickListener()
                {
                    public void onClick(DialogInterface dialog, int which)
                    {
                        //Nothing to do
                    }
                }).show();
    }
}

작업 예

public class ExampleTask extends ExceptionHandlingAsyncTask<String, Void, Result>
{
    private ProgressDialog  dialog;

    public ExampleTask(Context ctx)
    {
        super(ctx);
        dialog = new ProgressDialog(ctx);
    }

    @Override
    protected void onPreExecute()
    {
        dialog.setMessage(getResources().getString(R.string.dialog_logging_in));
        dialog.show();
    }

    @Override
    protected Result doInBackground2(String... params)
    {
        return new Result();
    }

    @Override
    protected void onPostExecute2(Result result)
    {
        if (dialog.isShowing())
            dialog.dismiss();
        //handle result
    }

    @Override
    protected void onPostException(Exception exception)
    {
        if (dialog.isShowing())
            dialog.dismiss();
        super.onPostException(exception);
    }
}

myActivity.getApplicationContext (). getResources ()에서와 같이 getResources () 메소드를 얻었습니다
Stephane

2

이 간단한 수업은 당신을 도울 수 있습니다

public abstract class ExceptionAsyncTask<Param, Progress, Result, Except extends Throwable> extends AsyncTask<Param, Progress, Result> {
    private Except thrown;

    @SuppressWarnings("unchecked")
    @Override
    /**
     * Do not override this method, override doInBackgroundWithException instead
     */
    protected Result doInBackground(Param... params) {
        Result res = null;
        try {
            res = doInBackgroundWithException(params);
        } catch (Throwable e) {
            thrown = (Except) e;
        }
        return res;
    }

    protected abstract Result doInBackgroundWithException(Param... params) throws Except;

    @Override
    /**
     * Don not override this method, override void onPostExecute(Result result, Except exception) instead
     */
    protected void onPostExecute(Result result) {
        onPostExecute(result, thrown);
        super.onPostExecute(result);
    }

    protected abstract void onPostExecute(Result result, Except exception);
}

2

가변 멤버 공유에 의존하지 않는 또 다른 방법은 cancel을 사용하는 것입니다.

이것은 안드로이드 문서에서 온 것입니다 :

공개 최종 부울 취소 (boolean mayInterruptIfRunning)

이 작업의 실행을 취소하려고합니다. 작업이 이미 완료되었거나 이미 취소되었거나 다른 이유로 취소 할 수없는 경우이 시도는 실패합니다. 성공하고 cancel이 호출 될 때이 태스크가 시작되지 않은 경우이 태스크는 절대 실행되지 않아야합니다. 작업이 이미 시작된 경우 mayInterruptIfRunning 매개 변수는 작업을 중지하려는 시도에서이 작업을 실행하는 스레드를 중단해야하는지 여부를 결정합니다.

이 메소드를 호출하면 doInBackground (Object [])가 반환 된 후 UI 스레드에서 onCancelled (Object)가 호출됩니다. 이 메소드를 호출하면 onPostExecute (Object)가 호출되지 않습니다. 이 메소드를 호출 한 후, doInBackground (Object [])에서 isCancelled ()에 의해 리턴 된 값을 점검하여 가능한 빨리 태스크를 완료해야합니다.

따라서 catch 문에서 cancel을 호출하고 onPostExcute가 호출되지 않지만 UI 스레드에서 onCancelled가 호출되는지 확인할 수 있습니다. 따라서 오류 메시지를 표시 할 수 있습니다.


문제 (예외)를 모르기 때문에 오류 메시지를 제대로 표시 할 수 없습니다. 여전히 AsyncTaskResult를 잡아서 반환해야합니다. 또한 사용자 취소는 오류가 아니며 예상되는 상호 작용입니다.이를 어떻게 구별합니까?
TWiStErRob

cancel(boolean)onCancelled()처음부터 호출이 있었지만 onCancelled(Result)API 11에 추가되었습니다 .
TWiStErRob

0

실제로 AsyncTask는 FutureTask & Executor를 사용하고 FutureTask는 예외 체인을 지원합니다. 먼저 도우미 클래스를 정의 해 봅시다.

public static class AsyncFutureTask<T> extends FutureTask<T> {

    public AsyncFutureTask(@NonNull Callable<T> callable) {
        super(callable);
    }

    public AsyncFutureTask<T> execute(@NonNull Executor executor) {
        executor.execute(this);
        return this;
    }

    public AsyncFutureTask<T> execute() {
        return execute(AsyncTask.THREAD_POOL_EXECUTOR);
    }

    @Override
    protected void done() {
        super.done();
        //work done, complete or abort or any exception happen
    }
}

둘째, 사용합시다

    try {
        Log.d(TAG, new AsyncFutureTask<String>(new Callable<String>() {
            @Override
            public String call() throws Exception {
                //throw Exception in worker thread
                throw new Exception("TEST");
            }
        }).execute().get());
    } catch (InterruptedException e) {
        e.printStackTrace();
    } catch (ExecutionException e) {
        //catch the exception throw by worker thread in main thread
        e.printStackTrace();
    }

-2

개인적으로이 접근법을 사용하겠습니다. 정보가 필요한 경우 예외를 잡아서 스택 추적을 인쇄 할 수 있습니다.

백그라운드에서 작업이 부울 값을 반환하도록합니다.

다음과 같습니다.

    @Override
                protected Boolean doInBackground(String... params) {
                    return readXmlFromWeb(params[0]);
         }

        @Override
                protected void onPostExecute(Boolean result) {

              if(result){
              // no error
               }
              else{
                // error handling
               }
}

-2

또 다른 가능성은 Object리턴 유형 으로 사용 onPostExecute()하고 오브젝트 유형 을 확인하는 것입니다. 짧습니다.

class MyAsyncTask extends AsyncTask<MyInObject, Void, Object> {

    @Override
    protected AsyncTaskResult<JSONObject> doInBackground(MyInObject... myInObjects) {
        try {
            MyOutObject result;
            // ... do something that produces the result
            return result;
        } catch (Exception e) {
            return e;
        }
    }

    protected void onPostExecute(AsyncTaskResult<JSONObject> outcome) {
        if (outcome instanceof MyOutObject) {
            MyOutObject result = (MyOutObject) outcome;
            // use the result
        } else if (outcome instanceof Exception) {
            Exception e = (Exception) outcome;
            // show error message
        } else throw new IllegalStateException();
    }
}

1
전혀 관련이 없습니다
Dinu

-2

올바른 예외를 알고 있다면

Exception e = null;

publishProgress(int ...);

예 :

@Override
protected Object doInBackground(final String... params) {

    // TODO Auto-generated method stub
    try {
        return mClient.call(params[0], params[1]);
    } catch(final XMLRPCException e) {

        // TODO Auto-generated catch block
        this.e = e;
        publishProgress(0);
        return null;
    }
}

"onProgressUpdate"로 가서 다음 작업을 수행하십시오.

@Override
protected void onProgressUpdate(final Integer... values) {

    // TODO Auto-generated method stub
    super.onProgressUpdate(values);
    mDialog.dismiss();
    OptionPane.showMessage(mActivity, "Connection error", e.getMessage());
}

일부 경우에만 도움이됩니다. 또한 Global Exception변수 를 유지 하고 예외에 액세스 할 수 있습니다 .


1
제발 이러지 마 정말, 정말 나쁜 스타일입니다!
JimmyB
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.