AsyncTask源码分析

AsyncTask是一种轻量级的异步类,在执行的过程中将执行的情况返回给主线程并在主线程更新UI

Example

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Here is an example of subclassing from Google:
private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
protected Long doInBackground(URL... urls) {
int count = urls.length;
long totalSize = 0;
for (int i = 0; i < count; i++) {
totalSize += Downloader.downloadFile(urls[i]);
publishProgress((int) ((i / (float) count) * 100));
// Escape early if cancel() is called
if (isCancelled()) break;
}
return totalSize;
}
protected void onProgressUpdate(Integer... progress) {
setProgressPercent(progress[0]);
}
protected void onPostExecute(Long result) {
showDialog("Downloaded " + result + " bytes");
}
}
Once created, a task is executed very simply:
DownloadFilesTask task1 = new DownloadFilesTask()
task1.execute(url1);
task1.execute(url2); // IllegalStateException("Cannot execute task: the task is already running."); or ("Cannot execute task: the task has already been executed a task can be executed only once)")

使用限制

  • 只能在主线程中创建对象
  • 只能在主线程执行execute()方法
  • 只能执行一次

工作原理

  • execute方法调用executeOnExecutor并传递默认线程池及params

    1
    2
    3
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
    }
  • executeOnExecutor判端当前状态是否为PENDING,否则抛出异常,封装params参数为mFuture对象(构造函数中将mWorker传递给mFuture),将该对象持有的mStatus对象改为RUNNING,导致一个AsyncTas只可执行一次。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    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;
    }
  • 线程池sDefaultExecutor(static,串行)开始执行

    1
    2
    3
    public static void execute(Runnable runnable) {
    sDefaultExecutor.execute(runnable);
    }
  • sDefaultExecutor将runnable放入一双端队列中,判端当前是否有活动的Runable对象,若没有就从队列中获取一不为空的Runable对象,并用线程池执行该对象。执行结束后再执行下一个Runnable对象。这就使AsyncTask串行execute()各个对象。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    private static class SerialExecutor implements Executor {
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
    Runnable mActive;
    public synchronized void execute(final Runnable r) {
    mTasks.offer(new Runnable() {
    public void run() {
    try {
    r.run();
    } finally {
    scheduleNext();
    }
    }
    });
    if (mActive == null) {
    scheduleNext();
    }
    }
    protected synchronized void scheduleNext() {
    if ((mActive = mTasks.poll()) != null) {
    THREAD_POOL_EXECUTOR.execute(mActive);
    }
    }
    }
  • 以上为使用execute()方法时执行情况,若执行executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,url1)方法则直接使用THREAD_POOL_EXECUTOR线程池并行执行url1、url2等任务。

    1
    2
    3
    4
    5
    6
    7
    8
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,Params... params) {
    if (mStatus != Status.PENDING) {
    ...
    }
    ...
    exec.execute(mFuture);
    return this;
    }

AsyncTask中的线程池

1
2
3
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE
,TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

AsyncTask中共有两个线程池SerialExecutor、THREAD_POOL_EXECUTOR,SerialExecutor串行执行各个AsyncTask对象,THREAD_POOL_EXECUTOR为并行执行的线程池,执行耗时操作

AsyncTask缺陷

由于最近要面试滴滴,发现面经上有此问题,故更新
1.生命周期
当前activity结束后,doInBackground仍会执行到结束,若没有cancel,将会执行onPostExecuted导致程序崩溃
2.内存泄漏
若在activity中采用静态内部类的asynctask,在finish后,asynctask仍持有activity的引用,导致内存溢出
3.并行串行
execute方法只能执行一次,且多个对象依次执行,并行只能采用executeOnExecutor()

更新于2017年11月3日