AsynctaskとAndroidでのエラー処理
-
20-09-2019 - |
質問
私は自分のコードを使用から変換しています Handler
に AsyncTask
. 。後者は、それが何をするのかに優れています - 非同期更新とメインUIスレッドでの結果の処理。私に不明なのは、何かがhaywireになった場合に例外を処理する方法です AsyncTask#doInBackground
.
私のやり方は、エラーハンドラーを持ち、メッセージを送信することです。それは正常に動作しますが、それは「正しい」アプローチですか、それともより良い代替手段がありますか?
また、エラーハンドラーをアクティビティフィールドとして定義する場合、UIスレッドで実行する必要があることを理解しています。しかし、時々(非常に予測不可能に)私は、コードがトリガーされたという例外を取得します Handler#handleMessage
間違ったスレッドで実行されています。エラーハンドラーを初期化する必要があります Activity#onCreate
代わりは?配置 runOnUiThread
の中へ Handler#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メソッドから返品し、事後録音で確認してください。 (このクラスを他の非同期タスクの基本クラスとして使用できます)
以下は、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
}
};
}
例外を処理する必要があると感じたとき 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に例外がスローされた場合、failure機能が例外に渡されます。そうしないと、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);
}
}
より包括的なソリューション 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;
}
}
例外Handlingasynctask
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);
}
変数メンバー共有に依存しない別の方法は、キャンセルを使用することです。
これはAndroidドキュメントからのものです。
パブリックファイナルブールキャンセル(Boolean may interrumpifrunning)
このタスクの実行をキャンセルしようとします。この試みは、タスクが既に完了した、すでにキャンセルされている、または他の理由でキャンセルできなかった場合に失敗します。成功し、キャンセルが呼び出されたときにこのタスクが開始されていない場合、このタスクは実行されないはずです。タスクが既に開始されている場合、May interntrumpifrunningパラメーターは、このタスクを実行するスレッドがタスクを停止しようとして中断するかどうかを決定します。
このメソッドを呼び出すと、doinbackground(object [])返品後にoncancelled(オブジェクト)がUIスレッドに呼び出されます。この方法を呼び出すと、OnPostExecute(オブジェクト)が呼び出されないことが保証されます。この方法を呼び出した後、Doinbackground(Object [])から定期的にIsCancelled()によって返された値を確認して、できるだけ早くタスクを完了する必要があります。
したがって、CACTステートメントでCANCELを呼び出して、OnPostExcuteが呼び出されることはないことを確認できますが、代わりにOnCancelledがUIスレッドで呼び出されます。したがって、エラーメッセージを表示できます。
実際、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
リターンタイプとして、およびin 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
変数と例外にアクセスします。