AsyncTask源码分析

2018-07-20  本文已影响0人  _Once1

使用示例

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]);
            // 此方法可在doInBackground中调用,发布进度更新
            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");
    }
}

// 调用方法: 
new DownloadFilesTask().execute(url1, url2, url3);

简述

AsyncTask中封装了两个线程池和一个handler,其中SerialExecutor用于任务的排队,而THREAD_POOL_EXECUTOR用于真正的执行任务
下面看一下该类中定义的属性

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
private static final int KEEP_ALIVE = 1;

// 真正用于执行任务的线程池
public static final Executor THREAD_POOL_EXECUTOR
    = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,  TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

可见,该线程池核心线程数目为CPU数目+1,最大线程数量为2*CPU + 1
然后看一下另一个用于任务排队的线程池:

// 该线程池只是用于任务的排队,真正执行任务的还是THREAD_POOL_EXECUTOR
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);
        }
    }
}

该线程池只是一个串行的线程池,一个进程中所有的AsyncTask全都在这个串行的池中排队执行
首先,会将一个任务插入任务队列中,如果此时没有正在执行的任务,则调用scheduleNext执行一个任务,当任务执行结束,继续回调该方法,直道所有任务都执行结束
由此可见,其是串行执行的


下面从execute()方法开始看起:
new DownloadFilesTask().execute(url1, url2, url3);

注意:该方法必须在主线程中调用,默认实现是在线程池中串行执行的,如果需要并行执行的效果,可以调用executeOnExecutor方法,传入THREAD_POOL_EXECUTOR

execute又调用了executeOnExecutor(sDefaultExecutor, params);方法
该方法内部的实现逻辑为:

  1. 首先,检查当前的执行状态,若正在执行或者已执行,则会报错
  2. 调用onPreExecute()
  3. 调用传入的线程池去执行任务
    下面再看sDefaultExecutor内部的实现
        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));
            }
        };

看一下call方法的逻辑:
调用doInBackground,之后,再将结果调用postResult方法发送出去

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

说到handler,也解释了AsyncTask 必须在主线程中加载的原因:
其内部的Handler是静态的,为了让其能成功切换执行环境,该Handler必须在主线程中创建,因此,该类必须在主线程中加载

消息发出之后,会回调finish方法

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

会根据是否取消,回调两种方法


总结
内部封装2线程池+handler,一个负责将任务串行排序,另一个负责真正执行任务;执行完毕后,通过Handler将结果,进度等回调出去。
自从3.0之后,默认是采用一个线程来串行执行任务,但仍可以通过调用executeOnExecutor方法,传入线程池来实现并行执行的效果


参考:《Android开发艺术探索》

上一篇 下一篇

猜你喜欢

热点阅读