看该篇文章前,我推荐了另外一篇博客,看完再来看该篇博客。
接下来,就来分析为什么AsyncTask只能被执行一次:
public final AsyncTask<Params, Progress, Result> execute(Params... params) { return executeOnExecutor(sDefaultExecutor, params); }
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; }
然后我将该段代码注释掉
// 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)"); // } // }
再回看该函数,里面执行doInBackground中最重要的代码就是
exec.execute(mFuture);那我猜想应该是mFuture出现了问题。看如下该图
Callable和Runnable是类似的,只是Runnable是调用run函数,Callable调用call函数,而且call函数会返回执行结果,FutureTask就是去获取执行结果的一个类。具体可以看这篇文章: Runnable、Callable、Executor、Future、FutureTask关系解读
FutureTask主要是在FutureTask的构造函数中传入callable。当通过ThreadPoolExecutor.execute(FutureTask)后,会调用FutureTask.run,在run中会调用Callable.call函数,执行完后会将callable清空,所以FutureTask只能被执行一次,第二次执行时会发现callable已经为空。看下代码
public void run() { if (state != NEW || !UNSAFE.compareAndSwapObject(this, runnerOffset, null, Thread.currentThread())) return; try { Callable<V> c = callable; if (c != null && state == NEW) { V result; boolean ran; try { result = c.call(); ran = true; } catch (Throwable ex) { result = null; ran = false; setException(ex); } if (ran) set(result); } } finally { // runner must be non-null until state is settled to // prevent concurrent calls to run() runner = null; // state must be re-read after nulling runner to prevent // leaked interrupts int s = state; if (s >= INTERRUPTING) handlePossibleCancellationInterrupt(s); } }
在set(Rsult)和setException(ex)中会调用finishCompletion()。
private void finishCompletion() { // assert state > COMPLETING; for (WaitNode q; (q = waiters) != null;) { if (UNSAFE.compareAndSwapObject(this, waitersOffset, q, null)) { for (;;) { Thread t = q.thread; if (t != null) { q.thread = null; LockSupport.unpark(t); } WaitNode next = q.next; if (next == null) break; q.next = null; // unlink to help gc q = next; } break; } } done(); callable = null; // to reduce footprint }
而且在AsyckTask中FutureTask被声明称final的,即只能被初始化一次。后来我做了如下改变,将FutureTask和Callable去掉final,在execute函数中去初始化它,如下代码所示:
public final MyAsyncTask<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 = new WorkerRunnable<Params, Result>() { public Result call() throws Exception { mTaskInvoked.set(true); android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_BACKGROUND); //noinspection unchecked return postResult(doInBackground(mParams)); } }; mFuture = new FutureTask<Result>(mWorker) { @Override protected void done() { try { postResultIfNotInvoked(get()); } catch (InterruptedException e) { android.util.Log.w(LOG_TAG, e); } catch (ExecutionException e) { throw new RuntimeException("An error occured while executing doInBackground()", e.getCause()); } catch (CancellationException e) { postResultIfNotInvoked(null); } } }; mWorker.mParams = params; exec.execute(mFuture); return this; }