AndroidでのAsyncTaskとエラー処理


147

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

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

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


なぜコードを変換したかったのですか?正当な理由はありましたか?
HGPB 2013年

4
@Haraldoそれは少なくとも私が感じる方法ですより良いコーディング実践です
Bostone

回答:


178

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

私が握るThrowableExceptionAsyncTask、インスタンス自体、その後にそれで何かをonPostExecute()私のエラー処理は、画面上のダイアログを表示するオプションを持っているので、。


8
鮮やかさ!ハンドラーでサルする必要はもうありません
Bostone 2009年

5
これは私がスロー可能または例外を保持する方法ですか?「バックグラウンド処理の結果を保持する独自の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 2011年

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

以下は、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
            }
        };

    }

1
私はそれが好きです。素敵なカプセル化。これは元の回答の言い換えなので、回答は
残り

これは、ジェネリックがどのように役立つかを示す非常に優れたデモです。それは複雑さの点で奇妙な匂いを投げていますが、私が本当にはっきりと表現できる方法ではありません。
num1 2011

4
ニースのアイデア、ただ一つの質問:なぜあなたが呼ぶんsuper()AsyncTaskResultクラスが何も拡張しないとき?
ドンターナー

7
「害はありません」-冗長なコードは常に可読性とメンテナンスに有害です。そこから出して!:)
ドンターナー

2
ソリューションが本当に気に入りました...考えてみましょう-C#の人たちは、C#に対応するBackgroundTaskネイティブ実装でまったく同じメソッドを使用しました...
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 14

@TWiStErRobその考えに問題はありません。私はparamsを使用しない傾向があるので、個人的な好みの問題だと思います。私が好むnew Task("Param").execute()オーバーnew Task().execute("Param")
sulai

5

他の利点をもたらすRoboGuiceフレームワークを使用したい場合は、追加のCallback 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コールバックが結果を渡します。なぜ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

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

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

これはandroid docsからです:

public final boolean cancel(boolean mayInterruptIfRunning)

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

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

したがって、catchステートメントでcancelを呼び出し、onPostExcuteが呼び出されないようにしてください。代わりに、onCancelledがUIスレッドで呼び出されます。したがって、エラーメッセージを表示できます。


問題(例外)がわからないため、エラーメッセージを適切に表示できません。AsyncTaskResultをキャッチして返す必要があります。また、ユーザーによるキャンセルはエラーではなく、予期される相互作用です。これらをどのように区別しますか?
TWiStErRob 14

cancel(boolean)の呼び出しonCancelled()は最初から存在していonCancelled(Result)ましたが、API 11で追加されました
TWiStErRob 2014

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 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.