programing tip

Android에서 AsyncTask 및 오류 처리

itbloger 2020. 6. 14. 11:04
반응형

Android에서 AsyncTask 및 오류 처리


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

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

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


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

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


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

    }

예외를 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이를 수행하기에 완벽한 장소입니다.


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


성공과 실패에 대한 콜백을 정의하는 인터페이스를 사용하여 자체 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);
    }
}

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

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

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

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

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

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

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

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

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


실제로 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();
    }

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

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

다음과 같습니다.

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

        @Override
                protected void onPostExecute(Boolean result) {

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

또 다른 가능성은 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();
    }
}

올바른 예외를 알고 있다면

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변수 를 유지 하고 예외에 액세스 할 수 있습니다 .

참고 URL : https://stackoverflow.com/questions/1739515/asynctask-and-error-handling-on-android

반응형