Android之AsyncTask源码分析(第五篇:execute方法只能执行一次的原因)
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方法……
更多相关文章
- android加密、签名相关
- Android(安卓)判断一个url是否有效
- Android标题栏和状态栏显示与否的设置&&& Button或者ImageButton
- Android读取properties配置文件的实例详解
- android studio (mac) 快捷键
- 详解Android(安卓)getWidth和getMeasuredWidth
- Android(安卓)监听网络状态方法详解
- Android读取文本文件中内容的方法
- Android(安卓)apk安装的几种方法