AsyncTask源码深入分析和巧记线程池

一、 AsyncTask源码分析:

AsyncTask是Android中用来执行异步任务的类,底层封装了线程池和Handler。虽然现在很少使用它,它的缺点也是很明显的,比如会导致严重的内存泄漏等。尽管如此,抱着学习的态度,它的源码还是值得分析的。首先还是略微就AsyncTask的基本使用来进行一些说明:

  • 它是一个抽象类,如果我们需要使用它需要创建一个类来继承AsyncTask。
  • AsyncTask有一个抽象方法,子类必须实现:
  1. doInBackground(Params... params):在子线程中执行(实际上是在AsyncTask底层维护的线程池中执行),参数params表示传入的参数。在此方法中可以通过publishProgress方法来更新任务的进度,publishProgress方法会导致onProgressUpdate方法的执行,另外此方法还有一个任务就是需要返回结果给onPostExecute()。

  2. 还有几个重要方法:
    onPreExecute():在主线程中执行,一般用于做一些准备工作
    onPostExecute():在主线程中执行,当异步任务执行结束之后此方法得到调用。
    onProgressUpdate():运行在主线程中,用来更新任务的进度;
    publishProgress():在doInBackground中调用,通知进度更新,从而导致onProgressUpdate()方法得到执行;
    execute(Params... params):用来开始异步任务;
    onCanceled():运行在主线程中,当异步任务被取消时,此方法会被调用,而onPostExecute方法不会被回调。
  • AsyncTask有三个泛型参数:<Params, Progress, Result>,分别表示的意思是:

Params:表示传入参数的类型,也就是execute、doInBackgroud方法的参数类型,一般为String类型,传入所需的url。
Progress:表示进度的类型,一般是Integer。
Result:表示返回结果的类型,是doInBackground方法的返回类型,也是onPostExecute方法的参数类型。

  • 下面给出一般使用的示例代码:

    public class DownloadTask extends AsyncTask<String, Integer, Integer>{
        protected void onPreExecute(){
            showDialog();
        }
    
        protected Integer doInBackground(String... params){
            //联网下载,在子线程中
        }
        protected void onPostExecute(Integer status){
            mDialog.dismiss();
        }
    
        protected void onProgressUpdate(Integer... progress){
            mProgressBar.setProgress(progress[0]);
        }
    }
    

    然后调用AsyncTask的execute(“http://www.baidu.com”)方法。这样就可以实现具体异步功能了。下面才是重点,才更有价值,进行源码分析(版本是25)。

1. 直接从execute()方法入手:

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

直接调executeOnExecutor方法

2. 看executeOnExecutor方法的具体实现:

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

首先根据状态来进行判断,如果已经在运行或者已经结束则直接抛出异常,不再往下进行。然后把mStatus状态置为运行状态。接着,我们会看到一个熟悉的方法->onPreExecute(),这个时候程序还是运行在主线程(前提是execute在主线程中调用的,并且官方文档也指明了这一点),正好印证了前面的结论:此方法是在异步任务开始前得到执行的,并且运行在主线程中。

3. 再往下看,重点是exec.excute(mFuture)这行代码,前一行代码暂且不看。先看exec是何方神圣:

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

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

注意这个sDefaultExecutor是static的

/**
* An {@link Executor} that executes tasks one at a time in serial
* order.  This serialization is global to a particular process.
*/
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

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

到这里,我们的分析流程就到了SerialExecutor这个类的execute()方法中。实际上就三个部分:

  • 造了一个匿名内部类(Runnable)的对象
  • 将其放入集合mTask中
  • 调用scheduleNext()方法。

第一次进来,mActive为null,则会调用scheduleNext方法。而scheduleNext方法呢?就是从集合中取出Runnable对象传入线程池执行。

4. 看线程池

/**
* An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR;

static {
    ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
        CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
        sPoolWorkQueue, sThreadFactory);
    threadPoolExecutor.allowCoreThreadTimeOut(true);
    THREAD_POOL_EXECUTOR = threadPoolExecutor;
}

对线程池不理解也没关系,可以将其看做是new Thread(new Runnable{})形式即可。换句话说,也就是从现在开始,程序的执行已经放在了子线程中。那到底在子线程中干了什么呢?我们来看前面创建的匿名内部类的run方法:直接调用传入的Runnable的run方法,执行完后接着调用scheduleNext方法。这样,我们就可以不断从mTask集合中不断取出异步任务,然后去执行。实现了一种类似递归的机制(和Handler在handleMessage方法中再次发消息的机制类似)。实际上,我们从这里也可看出:如果我们有多个异步任务,那么他们实际上是串行去处理的,并非并行(因为sDefaultExecutor是静态的)。 现在,我们就来看看传给SerialExecutor的execute方法的Runnable的run方法中到底干了什么(因为前面提到的匿名Runnable对象并没干实质性的工作,它的作用仅仅是负责当一个异步任务执行完成后,然后再去取其它任务让其得以执行。仔细想想,这个地方用到了代理模式)。

mWorker.mParams = params;
exec.execute(mFuture);

private final FutureTask<Result> mFuture;

那么mFuture在哪里初始化的呢?我们来看构造器。

/**
* 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);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };

    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 occurred while executing doInBackground()",
                    e.getCause());
            } catch (CancellationException e) {
                postResultIfNotInvoked(null);
            }
        }
    };
}

这个FutureTask实现了RunnableFuture接口,而RunnableFuture又是继承自Runnable接口。也就是说实际上我们就只需要看FutureTask的run方法是如何实现的就行了。

public void run() {
    if (state != NEW ||!U.compareAndSwapObject(this, RUNNER, 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);
    }
}

重点看两行:

Callable c = callable; 

result = c.call();

 调了callable的call方法,那么这个callable引用到底指向谁?我们来看FutureTask的构造器:

/**
* Creates a {@code FutureTask} that will, upon running, execute the
* given {@code Callable}.
*
* @param  callable the callable task
* @throws NullPointerException if the callable is null
*/
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

从构造器中传入的。

5. 我们再回到AsyncTask的构造器中:

mFuture = new FutureTask<Result>(mWorker) 

mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
        mTaskInvoked.set(true);
        Result result = null;
        try {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            //noinspection unchecked
            result = doInBackground(mParams);
            Binder.flushPendingCommands();
        } catch (Throwable tr) {
            mCancelled.set(true);
            throw tr;
        } finally {
            postResult(result);
        }
        return result;
    }
};

那现在我们就明白了,实质性工作是在这里的call方法中进行的:

  • 调用了熟悉的doInBackground方法
  • 调用了postResult()方法

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

    postResult就是在构造Message,然后通过Handler发送出去。分析也就到了Handler的实现上,阅读getHandler()的源码:

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }
    
    
    private static class InternalHandler extends Handler {
        public InternalHandler() {
            super(Looper.getMainLooper());
        }
    
        @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;
            }
        }
    }
    

根据消息的类型,执行不同的操作。在这里我们看到了熟悉的onProgressUpdate()方法得到了调用,还有几个熟悉的方法不是那么明显,就是这里的finish方法,即AysncTask的finish方法:

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

在这里我们看到了或者onPostExecute()或者onCancelled()得到了调用。

还有一个问题,咱们可以去思考一下,这个Handler最终是切换到了主线程去运行了吗?是,何以见得?当然是此处Handler的构造器

public InternalHandler() {
    super(Looper.getMainLooper());
}

InternalHandler的Looper是主线程的Looper。如果还不理解的话,就需要看Handler源码分析了。Handler源码分析

6. 适当总结整个工作流程:

首先,AsyncTask在其构造器中初始化了两个域,一个是WorkRunnable的子类,另一个是FutureTask的子类。其次,我们的分析要从execute开始: AsyncTask.execute()-> AsycnTask.executeOnExecutor(),然后根据当前AsyncTask的状态来区分,不是PENDING状态的都抛异常。然后改状态为运行,记录传入execute的参数到mWorker,同时回调onPreExecute()方法,最后启动线程池执行任务,这个线程池是串行执行任务的。在线程池执行任务过程中,会调用前面在构造器中初始化的FutureTask的run方法,在run方法中又会WorkRunnable的call方法,call方法里会回调doInBackground,最终执行FutureTask的done方法,在此方法中会通过Handler转换到主线程然后回调onPostExecute()方法。

二、线程池(以下部分摘自Android开发艺术探索)

1. 使用线程池的好处:

  • 重用线程池中的线程,避免因为线程的创建和销毁所带来的性能开销
  • 能有效控制线程池的最大并发数,避免大量的线程之间因互相抢占系统资源而导致的阻塞现象。
  • 能够对线程进行简单的管理,并提供定时执行以及指定间隔循环执行等功能

2. ThreadPoolExecutor的相关参数说明:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue) 
  • corePoolSize:线程池的核心数,默认情况下,核心线程会在线程中一直存活,即使它们处于闲置状态。如果allowCoreThreadTimeOut的属性设置为true,那么闲置的核心线程在等待新任务到来时会有超时策略,这个时间间隔由keepAliveTime所指定,当等待时间超过keepAliveTime所指定时长时,核心线程就会被终止
  • maximunPoolSize:线程池所能容纳的最大线程数,当活动线程达到这个数后,后续的新任务将会被阻塞
  • keepAliveTime:非核心线程闲置时的超时时长,超过这个时长,非核心线程就会被回收。
  • unit:用于指定keepAliveTime参数的时间单位
  • workQueue:线程池中的任务队列,通过线程池的execute方法提交的Runnable对象会存储在这个参数中
  • threadFactory:线程工厂,为线程池提供创建新线程的功能。ThreadFactory是一个接口,它只有一个方法:Thread newThread(Runnable r)

3. ThreadPoolExecutor执行任务时大致遵循如下规则:

  • 如果线程池中的线程数量未达到核心线程的数量,那么会直接启动一个核心线程来执行任务
  • 如果线程池中的线程数量已经达到或者超过核心线程数,那么任务会被插入到任务队列中排队等待执行
  • 如果步骤2中无法将任务插入到任务队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定最大值,那么就会立刻启动一个非核心线程来执行任务。
  • 如果在步骤3中线程数量已经达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor会调用RejectExecutionHandler的rejectedExecution方法来通知调用者。

4.在AysncTask中线程池的配置:

a)  核心线程数 = CPU核心数 + 1
b)  线程池最大线程数 = CPU核心数 * 2 + 1
c)  核心线程无超时机制,非核心线程在闲置的超时时间为1秒
d)  任务队列的容量为128

5. 线程池的分类

  • FixedThreadPool:线程数量固定的线程池。当线程处于空闲状态时,它们并不会被回收,除非线程池被关闭了。当所有的线程都处于活动状态时,新任务都会处于等待状态,直到有线程空闲出来。本质:只有核心线程,并且核心线程没有超时机制,任务队列也没有大小限制

    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
    
  • CachedThreadPool:线程数量不定,只有非核心线程,最大线程数为Integer.MAX_VALUE。当线程池中的线程都处于活动状态时,线程池会创建新的线程来处理任务,否则就会利用空闲的线程来处理新任务。空闲线程有超时机制,时长为60秒钟,超过60秒钟闲置线程就会被回收。任务队列为空集合。适合执行大量的耗时比较少的任务。OkHttp的Dispatcher中使用的就是类似线程池(2017.12.26日添加)。

  • ScheduledThreadPool:核心线程数固定,非核心线程数没有限制,非核心线程空闲时立即被回收
  • SingleThreadPool:只有一个核心线程。

6. 总结口诀,帮助记忆四种特殊线程池:

(姗姗KFC)   ------即ssfc,每句首字母,我有个女同学叫姗姗
Single专一爱一人
S原配妾即丢      -----原配:比喻成核心线程,妾比喻非核心线程
F妻定不要妾,来者不拒 ---备胎是指任务队列
C与妾情60秒,无备胎

猜你喜欢

转载自blog.csdn.net/xlh1191860939/article/details/74364645
今日推荐