Android之AsyncTask源码分析(第五篇:execute方法只能执行一次的原因)

(注意:本文基于API 28的源码分析,API 29上或其他平台的源码略有不同) 

前言

    当你调用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、我们调用的execute()方法,位于AsyncTask中的540行

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params); //此处为640行
    }

 

2、AsyncTask的640行执行的是executeOnExecutor()方法,executeOnExecutor()方法里做了什么?在executeOnExecutor()方法中找到了抛出异常对象的地方,请看下面代码中的注释

    @MainThread
    public final AsyncTask<Params, Progress, Result> 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  代表AsyncTask对象处于准备状态

RUNNING 代表AsyncTask对象处于运行状态

FINISHED  代表AsyncTask对象处于完成状态

public enum Status {
   
        PENDING,
        
        RUNNING,
        
        FINISHED,
    }

 

6、继续寻找mStatus什么时候被赋值为RUNNING的

在整个AsyncTask类中只有一处为mStatus赋值为RUNNING,那就是在executeOnExecutor()方法中……

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        
        ........

        mStatus = Status.RUNNING;   //看这里

        ........

        return this;
    }

 

7、答案明确

一个AsyncTask对象,它的execute()方法只能调用一次!再次调用execute()方法时,内部调用的executeOnExecutor()方法有一个判断逻辑,会根据mStatus的状态抛出一个IllegalStateException对象,告知调用者AsyncTask对象已经运行过了

当mStatus的状态为RUNNING或FINISHED时,都会抛出异常,告知调用者AsyncTask正在运行中、或者已经运行结束!

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在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;

 

想再执行一个任务怎么办??

答:再new一个AsyncTask对象,再次调用execute()方法……

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值