web-dev-qa-db-ja.com

AsyncTaskとエラー処理Android

コードをHandlerからAsyncTaskに変換しています。後者は、メインUIスレッドでの非同期の更新と結果の処理が優れています。私にとって不明確なのは、AsyncTask#doInBackgroundで何かがうまくいかない場合に例外を処理する方法です。

私がやる方法は、エラーハンドラを用意して、それにメッセージを送信することです。それはうまくいきますが、それは「正しい」アプローチですか、それともより良い代替手段がありますか?

また、エラーハンドラをアクティビティフィールドとして定義すると、UIスレッドで実行される必要があることも理解しています。ただし、Handler#handleMessageからトリガーされたコードが間違ったスレッドで実行されているという例外を受け取ることがあります(非常に予測不可能です)。代わりにActivity#onCreateでエラーハンドラーを初期化する必要がありますか? runOnUiThreadHandler#handleMessageに配置することは冗長に思えますが、非常に確実に実行されます。

146
Bostone

それはうまくいきますが、それは「正しい」アプローチですか、より良い代替手段がありますか?

Throwableインスタンス自体のExceptionまたはAsyncTaskを保持してから、onPostExecute()でそれを処理するため、エラー処理には次のオプションがあります。画面にダイアログを表示します。

174
CommonsWare

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で確認します。 (このクラスを他の非同期タスクの基本クラスとして使用できます)

以下は、Webサーバーから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
            }
        };

    }
135
Cagatay Kalan

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はそれを行うのに最適な場所です。

10
sulai

他の利点をもたらすRoboGuiceフレームワークを使用する場合は、追加のCallback onException()があるRoboAsyncTaskを試すことができます。本当にうまく機能し、私はそれを使用しています。 http://code.google.com/p/roboguice/wiki/RoboAsyncTask

5
ludwigm

成功と失敗のコールバックを定義するインターフェイスを持つ独自のAsyncTaskサブクラスを作成しました。したがって、AsyncTaskで例外がスローされると、onFailure関数に例外が渡されます。そうでない場合、onSuccessコールバックに結果が渡されます。なぜAndroidにはもっと良いものがないのは私を超えています。

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
ErlVolton

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

変数メンバーの共有に依存しない別の方法は、キャンセルを使用することです。

これはAndroid docs:

public final boolean cancel(boolean mayInterruptIfRunning)

このタスクの実行をキャンセルしようとします。タスクがすでに完了している場合、キャンセルされている場合、または他の何らかの理由でキャンセルできない場合、この試行は失敗します。成功し、キャンセルが呼び出されたときにこのタスクが開始されていない場合、このタスクは実行されません。タスクが既に開始している場合、mayInterruptIfRunningパラメーターは、タスクを停止しようとしてこのタスクを実行しているスレッドを中断する必要があるかどうかを決定します。

このメソッドを呼び出すと、doInBackground(Object [])が返された後にonCancelled(Object)がUIスレッドで呼び出されます。このメソッドを呼び出すと、onPostExecute(Object)が呼び出されないことが保証されます。このメソッドを呼び出した後、isCancelled()がdoInBackground(Object [])から定期的に返す値をチェックして、タスクをできるだけ早く終了する必要があります。

したがって、catchステートメントでcancelを呼び出して、onPostExcuteが呼び出されることはなく、代わりにonCancelledがUIスレッドで呼び出されることを確認できます。そのため、エラーメッセージを表示できます。

2
Ali

このシンプルなクラスはあなたを助けることができます

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
Denis

実際、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();
    }
0
Yessy