0、当你调用AsyncTask对象的execute()方法时,突然发生崩溃………………内心充满了不解,它抛出异常:java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
为什么会这样呢????????????
07-09 23:30:11.085 23377-23377/com.xx.cheez E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.xx.cheez, PID: 23377
java.lang.IllegalStateException: Cannot execute task: the task has already been executed (a task can be executed only once)
at android.os.AsyncTask.executeOnExecutor(AsyncTask.java:640)
at android.os.AsyncTask.execute(AsyncTask.java:595)
at com.wp.cheez.activity.FillSpaceActivity$MyBtnClickLis.onClick(FillSpaceActivity.java:60)
at android.view.View.performClick(View.java:6891)
at android.widget.TextView.performClick(TextView.java:12651)
at android.view.View$PerformClick.run(View.java:26083)
at android.os.Handler.handleCallback(Handler.java:789)
at android.os.Handler.dispatchMessage(Handler.java:98)
at android.os.Looper.loop(Looper.java:164)
at android.app.ActivityThread.main(ActivityThread.java:6938)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:327)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1374)
1、从上面的线程堆栈找到AsyncTask的源码
@MainThread
public final AsyncTask execute(Params... params) {
return executeOnExecutor(sDefaultExecutor, params); //此处为595行
}
上面的堆栈明确说明execute方法位于AsyncTask类的595行
2、595行执行的是executeOnExecutor方法,让我们去看看executeOnExecutor方法里面又干了什么?在executeOnExecutor方法中找到了抛出异常对象的地方,请看下面代码中的注释
@MainThread
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running."); //找到了抛出异常的地方
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
mStatus = Status.RUNNING;
onPreExecute(); //看到老朋友了是不是,模版方法模式用到了极致
mWorker.mParams = params;
exec.execute(mFuture);
return this;
}
3、发现AsyncTask的mStatus == RUNNING的时候,executeOnExecutor()方法会抛出java.lang.IllegalStateException: Cannot execute task: the task has already been executed..……此处省略好多字,让我们再看下关键代码,注意注释
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running."); //…………这里…………
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
4、那么AsyncTask对象持有的mStatus什么时候会等于RUNNING呢?此时需要去找一下mStatus是在哪个位置被赋值为RUNNING的!!!
首先发现mStatus的初始值是Status.PENDING,而且还加了volatile,保证共享变量mStatus的可见性与有序性(一个线程写入该共享变量,另一个线程一定能读到它的最新值,并且volatile会禁止编译器与处理器的重排序)
private volatile Status mStatus = Status.PENDING;
5、那么第4条中提到的Status.PEDING又是什么呢???
AsyncTask类定义了一个称做Status的内部枚举类,它专门用于标记AsyncTask对象的状态,可以看到一共有三个状态
PENDING 代表准备状态
RUNNING 代表运行状态
FINISHED 代表完成状态
public enum Status {
PENDING,
RUNNING,
FINISHED,
}
6、继续寻找mStatus什么时候被赋值为RUNNING的
在整个AsyncTask类中只有一处为mStatus赋值为RUNNING,那就是在executeOnExecutor方法中……
@MainThread
public final AsyncTask executeOnExecutor(Executor exec,
Params... params) {
........
mStatus = Status.RUNNING; //看这里
........
return this;
}
7、答案明确了
一个AsyncTask对象,它的execute方法只能调用一次!再次调用execute方法时,内部调用的executeOnExecutor方法有一个判断逻辑,会根据mStatus的状态抛出一个IllegalStateException对象
当mStatus的状态为RUNNING或FINISHED时,都会抛出异常
if (mStatus != Status.PENDING) {
switch (mStatus) {
case RUNNING:
throw new IllegalStateException("Cannot execute task:"
+ " the task is already running.");
case FINISHED:
throw new IllegalStateException("Cannot execute task:"
+ " the task has already been executed "
+ "(a task can be executed only once)");
}
}
8、重温下mStatus在AsyncTask对象生命周期内的状态变化(下面都是mStatus仅有的赋值代码)
调用execute方法前,mStatus的状态为PENDING(AsyncTask对象创建阶段赋值)
private volatile Status mStatus = Status.PENDING;
调用execute方法后,mStatus的状态更新为RUNNING(executeOnExecutor方法中赋值)
mStatus = Status.RUNNING;
所有AsyncTask的任务执行完成后,mSatus的状态又变为FINISHED(finish方法中赋值)
mStatus = Status.FINISHED;
7、解决办法
想再执行一个任务怎么办??
答:再new一个AsyncTask对象,再次调用execute方法……