AsyncTask和Android上的error handling
我使用Handler
将代码转换为AsyncTask
。 后者非常擅长 – 在主UI线程中asynchronous更新和处理结果。 我不清楚的是,如果在AsyncTask#doInBackground
发生exception,如何处理exception。
我这样做的方式是有一个error handling程序并发送消息。 它工作正常,但它是“正确”的方法还是有更好的select?
另外我明白,如果我将error handling程序定义为活动字段,它应该在UI线程中执行。 然而,有时(非常难以预料的),我会得到一个exception说,从Handler#handleMessage
触发的代码执行在错误的线程。 我应该在Activity#onCreate
初始化error handling程序吗? 将runOnUiThread
放入Handler#handleMessage
似乎是多余的,但它执行得非常可靠。
它工作正常,但它是“正确”的方法,是否有更好的select?
我坚持在AsyncTask
实例本身中的Throwable
或Exception
,然后在onPostExecute()
做一些事情,所以我的error handling可以select在屏幕上显示一个对话框。
创build一个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中检查它。 (您可以将此类用作其他asynchronous任务的基类)
以下是从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
exception时,我使用它作为超类:
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
来做后台工作,在需要的地方高兴地抛出exception。 然后你被迫执行onPostExecute
(因为它是抽象的),这轻轻提醒你处理所有types的Exception
,这是作为parameter passing。 在大多数情况下,exception会导致某种types的UI输出,所以onPostExecute
是一个完美的地方。
如果你想使用带来其他好处的RoboGuice框架,你可以尝试RoboAsyncTask,它有一个额外的Callback onException()。 工程真正好,我用它。 http://code.google.com/p/roboguice/wiki/RoboAsyncTask
另一种不依赖于可变成员共享的方式是使用取消。
这是从android文档:
public final boolean cancel(boolean mayInterruptIfRunning)
尝试取消执行此任务。 如果任务已经完成,已经被取消或者由于其他原因不能被取消,则该尝试将失败。 如果成功,并且此任务在调用取消时尚未启动,则此任务不应运行。 如果任务已经开始,那么mayInterruptIfRunning参数确定执行这个任务的线程是否应该被中断以试图停止任务。
调用此方法将导致在doInBackground(Object [])返回后,在UI线程上调用onCancelled(Object)。 调用这个方法可以保证onPostExecute(Object)永远不会被调用。 调用此方法后,应该定期从doInBackground(Object [])中检查isCancelled()返回的值,以尽早完成任务。
所以你可以在catch语句中调用cancel,并确保onPostExcute永远不会被调用,而是在UI线程上调用onCancelled。 所以你可以显示错误信息。
Cagatay Kalan的解决scheme更全面的解决scheme如下所示:
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); }
我用一个定义callback成功和失败的接口做了我自己的AsyncTask子类。 因此,如果在AsyncTask中引发exception,则onFailure函数将传递该exception,否则,onSuccesscallback会传递您的结果。 为什么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); } }
就我个人而言,我会用这种方法。 如果您需要信息,您可以捕捉exception并打印出堆栈跟踪。
让你的任务在后台返回一个布尔值。
就像这样:
@Override protected Boolean doInBackground(String... params) { return readXmlFromWeb(params[0]); } @Override protected void onPostExecute(Boolean result) { if(result){ // no error } else{ // error handling } }
另一种可能是将Object
用作返回types,并在onPostExecute()
检查对象types。 它很短。
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
variables并访问exception。