线程之AsyncTask的完全解析

来源:互联网 发布:算法求最小公倍数 编辑:程序博客网 时间:2024/06/08 13:58

1. 概述

AsyncTask是一种轻量的异步任务类,它中间封装了线程池和Handler,所以我们使用它可以更方便地执行后台线程操作和UI更新的切换。


2. 用法

AsyncTask是一个抽象的泛型类,它提供了Params、Progress、Result这三个泛型参数,其中Params是异步任务类参数的类型,Progress表示任务执行的进度类型,Result表示后台任务返回的结果类型。它有4个方法。

  • onPreExecute(),在执行任务之前的操作,运行在ui线程上。
  • doInBackground(),要执行的任务,运行在线程上。
  • onProgressUpdate(),进度更新,运行在ui线程上,需要在doInBackground手动调用publishProgress方法。
  • onPostExecute(),执行任务之后调用,运行在ui线程上。

3. 源码解析

3.1 线程池之ThreadPoolExecutor

在源码中可以看到这样一组变量定义:

private static final// 手机CPU数量int CPU_COUNT = Runtime.getRuntime().availableProcessors();// 线程池中核心线程数量的大小int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));// 线程池中最大线程数量int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;int KEEP_ALIVE_SECONDS = 30;ThreadFactory sThreadFactory = new ThreadFactory() {};

这上面所有的参数它们有一个共同的作用,就是利用这些参数实例化一个线程池

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

通过public static final的定义我们得知AsyncTask内部维护了一个静态线程池,默认情况下,AsyncTask的任务就是交给这个线程池来执行的。

从上面分析我们能知道AsyncTask内部维护了一个THREAD_POOL_EXECUTOR的线程池。

3.2 Executor接口的实现SerialExecutor

SerialExecutor是一个实现Executor的AsyncTask的静态内部类

private static class SerialExecutor implements Executor {    // 一个双端队列,用来存放任务Rnnable类    ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();    // 当前正在执行的Runnable         Runnable mActive;    // 我们调用SerialExecutor的execute()会将Runnable再次封装,将其放入到双端队列mTasks的最后面。判断mActive是否为null来判断当前是否有任务在执行,如果没有任务在执行那么从任务队列中去一个任务去执行,如果有任务在执行则等待这个任务执行完毕后在finally中去取下一个任务    public synchronized void execute(final Runnable r) {        mTasks.offer(new Runnable() {            public void run() {                try {                    r.run();                } finally {                    scheduleNext();                }            }        });        if (mActive == null) {            scheduleNext();        }    }    // mTasks取出一个Runnable将其交给mActive,然后再交由THREAD_POOL_EXECUTOR线程池来执行    protected synchronized void scheduleNext() {        if ((mActive = mTasks.poll()) != null) {            THREAD_POOL_EXECUTOR.execute(mActive);        }    }}

从上面的代码分析中我们可以知道,当mTasks中的runnable作为参数传递给THREAD_POOL_EXECUTOR执行execute方法时,会在线程池的工作线程中执行匿名内部类Runnable中的try-finally代码段,即在工作线程中执行.run()方法,可以看到不管是正常执行还是抛出异常最终都会调用scheduleNext()方法,用来继续将mTasks中的下一个runnable传递出去,所以我们能到处结论,SerialExecutor是一个一个执行任务的是串行执行而非并行执行的。

另外,SerialExecutor将mTasks中的Runnable交给了THREAD_POOL_EXECUTOR去执行,说明SerialExecutor中的任务实际上是由THREAD_POOL_EXECUTOR线程池来处理的。

3.3 AsyncTask定义的字段

AsyncTask中定义的字段有这些

// 一个静态内部类,它绑定了UI线程private static InternalHandler sHandler;// 通过handler发布result的message codeprivate static final int MESSAGE_POST_RESULT = 0x1;// 通过handler发布progress的message codeprivate static final int MESSAGE_POST_PROGRESS = 0x2;// AsyncTask默认使用SERIAL_EXECUTOR作为它的Executor,所以默认情况下AsyncTask是串行而非并行执行的private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;// 是一个实现了Callable接口的对象private final WorkerRunnable<Params, Result> mWorker;// Future利用mWorker实例化private final FutureTask<Result> mFuture;// AsyncTask默认是未开始状态private volatile Status mStatus = Status.PENDING;// 任务是否被取消的标识private final AtomicBoolean mCancelled = new AtomicBoolean();// 任务是否真正开始了的标识private final AtomicBoolean mTaskInvoked = new AtomicBoolean();

3.3.1 表示状态的枚举类型

AsyncTask内部定义了一个枚举类型用来表示任务执行的状态。

// 分别表示还没有执行任务、正在执行任务、任务结束public enum Status {    PENDING, RUNNING, FINISHED,}

AsyncTask的状态一定是这样变化的:PENDING->RUNNING->FINISHED。

3.3.2 默认线程池

sDefaultExecutor的初始值为SERIAL_EXECUTOR,利用我们对SERIAL_EXECUTOR的理解我们知道AsyncTask默认是串行执行的。

3.3.3 InternalHandler

我们之前在提到AsyncTask的概念时说过,AsyncTask封装了线程池和Handler,这个sHandler就是AsyncTask的静态内部类InternalHandler

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

我们通过InternalHandler构造函数可以知道,sHandler和ui线程上的Looper相关联所以说明sHandler绑定了ui线程。

3.3.4 mWorker和mFuture

mWorker是一个WorkerRunnable的类,WorkerRunnable是实现Callable接口的抽闲类,Callable和Runnable相似,Runnable内部定义了run方法,Callable内部定义了call方法,call方法可以有返回值,但run方法不能有返回值。

mFuture是FutureTask的对象,由于Executor的execute方法需要接收Runnable对象,但是再执行完任务之后我们又需要返回result,这是变利用了FutureTask对象,它同时实现了Callable和Runnable接口,它的构造函数需要传入一个Callable对象,所以我们也可以将FutureTask对象mFuture传递给Executor的execute方法去执行。

当任务执行完之后会调用FutureTask的done方法,在任务执行的过程中还可以随时调用FutureTask的cancel方法取消执行任务,取消任务之后仍然会调用done方法。

3.4 AsyncTask的构造函数

AsyncTask的构造函数实际上就是对mWorker和mFuture的实例化。

mWorker的实例化

mWorker = new WorkerRunnable<Params, Result>() {    public Result call() throws Exception {        // 将任务开始标识设为true        mTaskInvoked.set(true);        Result result = null;        // 将call方法设置为后台线程级别        try {                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);            // 在线程池中执行doInBackground()方法,并返回result            result = doInBackground(mParams);            Binder.flushPendingCommands();        } catch (Throwable tr) {            mCancelled.set(true);            throw tr;        } finally {            // 将结果交由postResult()方法            postResult(result);        }        return result;    }};

mFuture的实例化

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);        } catch (CancellationException e) {            postResultIfNotInvoked(null);        }    }};

mWorker其实是一个Callable类型的对象。实例化mWorker,实现了Callable接口的call方法。call方法是在线程池的某个线程中执行的,而不是运行在主线程中。在线程池的工作线程中执行doInBackground方法,执行实际的任务,并返回结果。当doInBackground执行完毕后,将执行完的结果传递给postResult方法。postResult方法我们后面会再讲解。

mFuture是一个FutureTask类型的对象,用mWorker作为参数实例化了mFuture。在这里,其实现了FutureTask的done方法,我们之前提到,当FutureTask的任务执行完成或任务取消的时候会执行FutureTask的done方法。done方法里面的逻辑我们稍后再将。

3.5 AsyncTask.execute()

我们来看一下execute是如何运作的

3.5.1 executeOnExecutor()

在实例化AsyncTask之后需要调用AsyncTask的execute方法来执行任务

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

可以看到实际上execute调用了executeOnExecutor方法。

@MainThreadpublic final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,        Params... params) {    if (mStatus != Status.PENDING) {        switch (mStatus) {            case RUNNING:                throw new IllegalStateException("");            case FINISHED:                throw new IllegalStateException("");        }    }    // 在开始执行任务之前将状态改变    mStatus = Status.RUNNING;    // 真正执行任务之前调用该方法    onPreExecute();    //     mWorker.mParams = params;    exec.execute(mFuture);    return this;}

mStatus状态如果不是PENDING就抛出异常,说明AsyncTask只可以执行一次任务。

因为@MainThread我们知道executeOnExecutor()方法是运行在ui线程上的,所以onPreExecute()也是运行在ui线程上的。

再之后,调用了exec.execute(mFuture),开始执行任务,由于exec默认是SERIAL_EXECUTOR,所以会将包含任务的mFuture放入到静态队列中去,根据SERIAL_EXECUTOR的队则去排队执行任务。

开始执行任务之后,会调用mWorker中的call方法,从而执行doInBackground()方法之后,会将执行结果传递给postResult()方法。

3.5.2 postResult()

postResult()方法的代码如下

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

它会通过sHandler来发送带有Result的信息给ui线程,这其中出现了AsyncTaskResult,它是什么?

3.5.3 AsyncTaskResult

private static class AsyncTaskResult<Data> {    final AsyncTask mTask;    final Data[] mData;    AsyncTaskResult(AsyncTask task, Data... data) {        mTask = task;        mData = data;    }}

mTask表明了它是哪个AsyncTask的结果,mData表示存储的数据

3.5.4 sHandler的handleMessage()

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

执行result.mTask.finish()方法,就是执行当前AsyncTask的finish方法

3.5.5 finish()

private void finish(Result result) {        if (isCancelled()) {            //如果任务被取消了,那么执行onCancelled方法            onCancelled(result);        } else {            //将结果发传递给onPostExecute方法            onPostExecute(result);        }        //最后将AsyncTask的状态设置为完成状态        mStatus = Status.FINISHED;    }

finish方法一目了然,如果任务被取消了则执行onCancelled()方法,如果没有则执行onPostExecute()方法,因为sHandler是和ui线程关联的,所以这两个方法都运行在ui线程中。

3.5.6 done()

在mWorker的call()执行完毕之后,会调用mFuture的done()方法,我们查看源码知道不管任务是正常执行完毕还是被取消都会调用postResultIfNotInvoked()方法。

private void postResultIfNotInvoked(Result result) {    final boolean wasTaskInvoked = mTaskInvoked.get();    if (!wasTaskInvoked) {        //只有mWorker的call没有被调用才会执行postResult方法        postResult(result);    }}

我们在mWorker的call方法中将mTaskInvoked设为true,所以如果mWorker的call方法没有被执行则会调用postResult()方法。

如果AsyncTask正常执行完成的时候,call方法都执行完了,mTaskInvoked设置为true,并且在call方法中最后执行了postResult方法,然后进入mFuture的done方法,然后进入postResultIfNotInvoked方法,由于mTaskInvoked已经执行,所以不会执行再执行postResult方法。

如果在调用了AsyncTask的execute方法后立马就执行了AsyncTask的cancel方法(实际执行mFuture的cancel方法),那么会执行done方法,且捕获到CancellationException异常,从而执行语句postResultIfNotInvoked(null),由于此时还没有来得及执行mWorker的call方法,所以mTaskInvoked还未false,这样就可以把null传递给postResult方法。

由此AsyncTask的细节应该都清楚了。


4. 加深理解

4.1 例子

我们知道串行执行任务调用execute(),并发执行时调用executeOnExecutor()方法。

调用execute时,默认会将sDefaultExecutor设置为SERIAL_EXECUTOR,SERIAL_EXECUTOR是一个串行执行的过程。

调用executeOnExecutor(sDefaultExecutor, xx),我们将sDefaultExecutor变量设置为我们传递进去的线程池,或者直接使用AsyncTask内部维护的线程池ThreadPoolExecutor,这样任务的传递给ThreadPoolExecutor的execute()方法,它的方法则是一个并行执行的过程。

为了验证这个结论,我们写下了这几个测试的伪代码

class MyAsyncTask01 extends AsyncTask<String, Object, String>{        @Override        protected void onPreExecute() {            Log.i(Tag, "01-onPreExecute");            super.onPreExecute();        }        @Override        protected String doInBackground(String... params) {            for ( String string : params ){                Log.i(Tag, "01-doInBackground:" + string);                Thread.sleep(5000);            }            return "完成";        }        @Override        protected void onPostExecute(String s) {            super.onPostExecute(s);            Log.i(Tag, "01-onPostExecute:" + s);        }}class MyAsyncTask02 extends AsyncTask<String, Object, String>{    @Override    protected void onPreExecute() {        Log.i(Tag, "02-onPreExecute");        super.onPreExecute();    }    @Override    protected String doInBackground(String... params) {        for ( String string : params ){            Log.i(Tag, "02-doInBackground:" + string);        }        return "完成";    }    @Override    protected void onPostExecute(String s) {        super.onPostExecute(s);        Log.i(Tag, "02-onPostExecute:" + s);    }}MyAsyncTask01 myTask01 = new MyAsyncTask01();MyAsyncTask02 myTask02 = new MyAsyncTask02();

运行:

// 第1组myTask01.execute("a");        myTask02.execute("b");// 第2组myTask01.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "a");myTask02.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "b");

第1组

这里写图片描述

因为它是串行执行的,所以等待任务1执行完毕后才会执行任务2。

第2组

这里写图片描述

它是并行执行的,所以任务1和任务2是同时执行的。


4.2 doInbackground()竟然没有执行

我们知道并行执行要调用executeOnExecutor()方法,但是我的doInbackgroud方法竟然没有执行或者是等了很久才执行,其实原因这样的。AsyncTask内部维护了一个静态线程池ThreadPoolExecutor,因为它是静态的,所以它内部管理的不仅仅是我们定义AsyncTask任务的线程,它还有可能管理其他任务的线程,假如app后台运行了很多下载任务,任务都是利用AsyncTask并发下载的,ThreadPoolExecutor线程池中没有空闲线程了,这时候我们开启一个异步任务AsyncTask,由于没有空闲线程,所以你的doInbackground()方法需要等待有线程空闲下来才开始执行,才造成了以上的情况。我们可以模拟一下这个情况。

for (int i=0; i<100; i++){    MyAsyncTask01 t1 = new MyAsyncTask01();    t1.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "a");}MyAsyncTask02 t2 = new MyAsyncTask02();t2.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "b");

这里写图片描述

由图可见,足足过了25秒,t2任务才开始执行。这时候我们应该讲线程池换成我们自定义的线程池。


原创粉丝点击