AsyncTask源码分析

  1. 云栖社区>
  2. 博客>
  3. 正文

AsyncTask源码分析

lauren_liuling 2016-04-27 14:03:37 浏览1194
展开阅读全文

关于AsyncTask的用法可以参看前面一篇博客《AsyncTask实现断点续传》,本文只解析AsyncTask的源代码。

AsyncTask.execute方法:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {
	return executeOnExecutor(sDefaultExecutor, params);
}

execute方法里面直接调用了executeOnexecute方法。

AsyncTask.executeOnexecute方法:

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-13行是检测AsyncTask的状态,如果状态不为PENDING,则会抛异常,这也是为什么一个AsyncTask只能被执行一次的原因。14行将状态改为RUNNING,表示该任务正在运行。然后调用AsyncTask的onPreExecute()方法。

由下面代码可以看出,AsyncTask有三种状态:PENDING(未运行)、RUNNING(正在运行)、FINISHED(已运行完毕)。

public enum Status {
    /**
     * Indicates that the task has not been executed yet.
     */
    PENDING,
    /**
     * Indicates that the task is running.
     */
    RUNNING,
    /**
     * Indicates that {@link AsyncTask#onPostExecute} has finished.
     */
    FINISHED,
}

FutureTask代码:

public class FutureTask<V> implements RunnableFuture<V> {
    ......
    //构造方法传入一个Callable对象
    public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of 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();//这里调用了callable.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);
        }
    }
    ......
}

AsyncTask构造方法:

public abstract class AsyncTask<Params, Progress, Result> {
    ......
    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                return postResult(doInBackground(mParams));
            }
        };
        //创建FutureTask对象的时候传入了mWorker作为Callable
        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);
                }
            }
        };
    }
    ......
}

由FutureTask源码我们可以看出,run()方法里面调用了c.call(),而AsyncTask 中创建FutureTask的时候传入了mWorker,所以FutureTask.run()方法里面c.call()调用的是mWorker对象的 call()方法,而AsyncTask里mWorker重写了call方法,即上面8-14行,所以c.call()会执行到 mWorker.call()方法来。call方法里面11行将线程的优先级设置为后台线程,这样当多个线程并发后很多无关紧要的线程分配的CPU时间将 会减少,有利于主线程的处理。

接下来11行执行了doInBackground(mParams)方法,通常我们会重写该方法来实现业务逻辑操作。然后执行postResult 方法,并且将结果返回给FutureTask(因为是FutureTask.run方法调用的此call方法,所以需要返回结果到 FutureTask.run方法)。这里我们先看看postResult:

private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

这里的sHandler是InternalHandler对象。

private static class InternalHandler extends Handler {
    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult result = (AsyncTaskResult) msg.obj;
        switch (msg.what) {
            case MESSAGE_POST_RESULT:
                // There is only one result
                result.mTask.finish(result.mData[0]);
                break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

由第9行代码可知最终会执行AsyncTask的finish方法,代码如下:

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

finish的作用是如果task被取消了就执行onCancelled方法,如果没有被取消而是正常执行完毕,则执行onPostExecute 方法(这也是为什么task被调用了cancel方法,不会执行onPostExecute的原因)。最后将task的状态标记为FINISHED。

上面说到mWorker.call会将执行结果返回给FutureTask.run()方法并且继续往下执行,我们再次看看FutureTask.run方法(20-30行):

boolean ran;
try {
     result = c.call();
     ran = true;
} catch (Throwable ex) {
     result = null;
     ran = false;
     setException(ex);
}
if (ran)
     set(result);

由上面代码可以看到,执行完c.call后,会执行set(result)方法。

protected void set(V v) {
    if (UNSAFE.compareAndSwapInt(this, stateOffset, NEW, COMPLETING)) {
        outcome = v;
        UNSAFE.putOrderedInt(this, stateOffset, NORMAL); // final state
        finishCompletion();
    }
}

最终会执行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
}

看到21行代码,会执行FutureTask的done()方法,而这个方法在AsyncTask构造函数中初始化FutureTask对象的时候被重写了。

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);
        }
    }
};

这里主要是验证postResult是否被调用了,如果没有被调用着调用postResult函数,因为前面mWorker.call方法里面调用过了,所以这里不错操作。

顺便提一下,在AsyncTask的doInBackground方法中如果需要更新UI的话,则调用AsyncTask的publishProgress方法即可:

protected final void publishProgress(Progress... values) {
     if (!isCancelled()) {
         sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                 new AsyncTaskResult<Progress>(this, values)).sendToTarget();
     }
}

publishProgress方法最终也会通过sHandler来调用AsyncTask的onProgressUpdate方法,一般我们如果需要获取进度的话都需要重写AsyncTask的onProgressUpdate。

好了,AsyncTask的源码也分析完了。再次总结一下Asynctask使用的注意事项:

  1. 异步任务的实例必须在UI线程中创建。
  2. execute(Params… params)方法必须在UI线程中调用。
  3. 不要手动调用onPreExecute(),doInBackground(Params… params),onProgressUpdate(Progress… values),onPostExecute(Result result)这几个方法。
  4. 不能在doInBackground(Params… params)中更改UI组件的信息。
  5. 一个任务实例只能执行一次,如果执行第二次将会抛出异常。

网友评论

登录后评论
0/500
评论
lauren_liuling
+ 关注