Android 上的 AsyncTask 和错误处理
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1739515/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
AsyncTask and error handling on Android
提问by Bostone
I'm converting my code from using Handler
to AsyncTask
. The latter is great at what it does - asynchronous updates and handling of results in the main UI thread. What's unclear to me is how to handle exceptions if something goes haywire in AsyncTask#doInBackground
.
我正在将我的代码从 using 转换Handler
为AsyncTask
. 后者非常擅长它的工作 - 在主 UI 线程中异步更新和处理结果。我不清楚的是,如果AsyncTask#doInBackground
.
The way I do it is to have an error Handler and send messages to it. It works fine, but is it the "right" approach or is there better alternative?
我这样做的方法是有一个错误处理程序并向它发送消息。它工作正常,但它是“正确”的方法还是有更好的选择?
Also I understand that if I define the error Handler as an Activity field, it should execute in the UI thread. However, sometimes (very unpredictably) I will get an Exception saying that code triggered from Handler#handleMessage
is executing on the wrong thread. Should I initialize error Handler in Activity#onCreate
instead? Placing runOnUiThread
into Handler#handleMessage
seems redundant but it executes very reliably.
我也明白,如果我将错误处理程序定义为 Activity 字段,它应该在 UI 线程中执行。但是,有时(非常不可预测)我会收到一个异常,说明触发的代码Handler#handleMessage
在错误的线程上执行。我应该初始化错误处理程序Activity#onCreate
吗?放置runOnUiThread
到Handler#handleMessage
似乎是多余的,但执行非常可靠。
回答by CommonsWare
It works fine but is it the "right" approach and is there better alternative?
它工作正常,但它是“正确”的方法吗?还有更好的选择吗?
I hold onto the Throwable
or Exception
in the AsyncTask
instance itself and then do something with it in onPostExecute()
, so my error handling has the option of displaying a dialog on-screen.
我保留实例本身中的Throwable
或Exception
,AsyncTask
然后在 中用它做一些事情onPostExecute()
,所以我的错误处理可以选择在屏幕上显示一个对话框。
回答by Cagatay Kalan
Create an AsyncResult object ( which you can also use in other projects)
创建一个 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;
}
}
Return this object from your AsyncTask doInBackground methods and check it in the postExecute. ( You can use this class as a base class for your other async tasks )
从您的 AsyncTask doInBackground 方法返回此对象并在 postExecute 中检查它。(您可以将此类用作其他异步任务的基类)
Below is a mockup of a task that gets a JSON response from the web server.
下面是一个从 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
}
};
}
回答by sulai
When I feel the need to handle Exceptions in AsyncTask
properly, I use this as super class:
当我觉得需要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;
}
}
As normal, you override doInBackground
in your subclass to do background work, happily throwing Exceptions where needed. You are then forced to implement onPostExecute
(because it's abstract) and this gently reminds you to handle all types of Exception
, which are passed as parameter. In most cases, Exceptions lead to some type of ui output, so onPostExecute
is a perfect place to do that.
像往常一样,您doInBackground
在子类中覆盖以执行后台工作,并在需要时愉快地抛出异常。然后您被迫实现onPostExecute
(因为它是抽象的),这会轻轻提醒您处理Exception
作为参数传递的所有类型。在大多数情况下,异常会导致某种类型的 ui 输出,因此这onPostExecute
是一个完美的地方。
回答by ludwigm
If you want to use the RoboGuice framework which brings you other benefits you can try the RoboAsyncTask which has an extra Callback onException(). Works real good and I use it. http://code.google.com/p/roboguice/wiki/RoboAsyncTask
如果您想使用为您带来其他好处的 RoboGuice 框架,您可以尝试具有额外回调 onException() 的 RoboAsyncTask。效果很好,我使用它。 http://code.google.com/p/roboguice/wiki/RoboAsyncTask
回答by ErlVolton
I made my own AsyncTask subclass with an interface that defines callbacks for success and failure. So if an exception is thrown in your AsyncTask, the onFailure function gets passed the exception, otherwise the onSuccess callback gets passed your result. Why android doesn't have something better available is beyond me.
我使用定义成功和失败回调的接口创建了自己的 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);
}
}
回答by vahapt
A more comprehensive solution to Cagatay Kalan's solution is shown below:
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();
}
}
Example Task
示例任务
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);
}
}
回答by Ali
Another way that doesn't depend on variable member sharing is to use cancel.
另一种不依赖于变量成员共享的方法是使用取消。
This is from android docs:
这是来自 android 文档:
public final boolean cancel (boolean mayInterruptIfRunning)
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
Calling this method will result in onCancelled(Object) being invoked on the UI thread after doInBackground(Object[]) returns. Calling this method guarantees that onPostExecute(Object) is never invoked. After invoking this method, you should check the value returned by isCancelled() periodically from doInBackground(Object[]) to finish the task as early as possible.
public final boolean cancel (boolean mayInterruptIfRunning)
尝试取消此任务的执行。如果任务已完成、已被取消或由于其他原因无法取消,则此尝试将失败。如果成功,并且在调用取消时此任务尚未启动,则不应运行此任务。如果任务已经开始,则 mayInterruptIfRunning 参数确定是否应该中断执行此任务的线程以尝试停止任务。
调用此方法将导致在 doInBackground(Object[]) 返回后在 UI 线程上调用 onCancelled(Object)。调用此方法可确保永远不会调用 onPostExecute(Object)。调用此方法后,应定期从 doInBackground(Object[]) 中检查 isCancelled() 返回的值,以尽早完成任务。
So you can call cancel in catch statement and be sure that onPostExcute is never called, but instead onCancelled is invoked on UI thread. So you can show the error message.
因此,您可以在 catch 语句中调用取消并确保从不调用 onPostExcute,而是在 UI 线程上调用 onCancelled。所以你可以显示错误信息。
回答by Denis
This simple class can help you
这个简单的课程可以帮助你
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);
}
回答by Yessy
Actually, AsyncTask use FutureTask & Executor, FutureTask support exception-chain First let's define a helper class
实际上,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
}
}
Second, let's use
其次,让我们使用
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();
}
回答by Matthias Ronge
Another possibility would be to use Object
as return type, and in onPostExecute()
check for the object type. It is short.
另一种可能性是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();
}
}