Android AsyncTask的使用及源码分析

在上一篇博客中,我们提到AsyncTask的任务默认是串行执行的,并且AsyncTask对象必须在主线程中创建,并且一个AsyncTask对象只能执行一次execute方法,否则就会报错,这一切的原因是什么呢?本文就将通过源码分析来解释这几个问题。

首先来再看一下AsyncTask的几个核心方法:

  • excute: 创建好AsyncTask对象后,需要调用此方法才开始执行任务;
  • onPreExecute:在主线程中执行,在异步任务执行之前执行,可以做一些准备工作;
  • doInBackground:在线程池中执行,执行具体的异步任务。在这个方法中可以调用publishProgress方法来更新任务的进度;
  • onProgressUpdate:在主线程中执行,调用publishProgress方法后会被调用;
  • onPostExecute:在主线程中执行,异步任务执行完毕后会被调用,返回执行结果;
  • onCancelled:在主线程中执行,异步任务被取消时会被调用,这样onPostExecute方法将不会被调用;

以上方法中,只有doInBackground方法是在线程池中去执行的,毕竟耗时操作也是在这个方法中去完成的。

1、AsyncTask的使用

这里使用了前文所用到的例子,只是做一个演示, 示例代码如下:

private class MyTask extends AsyncTask<Params, Progress, Result> {

    ....

  // 作用:执行 线程任务前的操作
  @Override
  protected void onPreExecute() {
       ...
    }

  // 作用:接收输入参数、执行任务中的耗时操作、返回 线程任务执行的结果
  @Override
  protected String doInBackground(String... params) {
        ...
        // 可调用publishProgress()显示进度, 之后将执行onProgressUpdate()
         publishProgress(count);     
     }

  // 作用:在主线程 显示线程任务执行的进度
  @Override
  protected void onProgressUpdate(Integer... progresses) {
        ...
    }

  // 作用:接收线程任务执行结果、将执行结果显示到UI组件
  @Override
  protected void onPostExecute(String result) {

     ...// UI操作

    }

  // 作用:将异步任务设置为:取消状态
  @Override
    protected void onCancelled() {
    ...
    }
}

  MyTask mTask = new MyTask();
  mTask.execute("传入参数");
...

2、源码分析

通过上述事例代码,我们发现,整个AsyncTask的工作是从调用execute方法开始的,所以从这里开始追踪代码,execute源码如下:

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

看注解,已经标注了需要在主线程中去执行execute方法,这里的源码只有一行,但需要分成两步去看:

  • 1、调用了 executeOnExecutor方法;
  • 2、传入了sDefaultExecutor这个线程池;

2.1 sDefaultExecutor线程池

这是一个串行的线程池,我们**整个进程的所有AsyncTask任务全部都在这个串行的线程池中去执行**,所以说AsyncTask不适合做特别耗时的任务原因也在于此,这个线程池的源码如下:

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() {//此处通过offer方法添加到队列中,并没有立即执行
            public void run() {
                try {
                    r.run();//1、执行当前任务
                } finally {
                    scheduleNext(); //2、执行完任务后,取出下一个任务继续执行, 这一段代码是同步的,所以只能顺序执行
                }
            }
        });
        if (mActive == null) {
            scheduleNext();//如果当前没有任务在执行,则去取出任务执行
        }
    }

    protected synchronized void scheduleNext() {
        if ((mActive = mTasks.poll()) != null) {
            THREAD_POOL_EXECUTOR.execute(mActive);//通过THREAD_POOL_EXECUTOR线程池去执行任务
        }
    }
}

从源码可看到,这个线程池的execute是用synchronize加锁了的,所以一次也只能执行一个任务, 所有的任务都存储在mTask这个双端队列中,按照先入先出来执行。当来了一个新任务后,直接通过offer添加到mTasks中, 等同前面先加入的任务执行完后在执行,如果当前没有任务执行,则直接调用scheduleNext()开始去执行。并且,真正的执行是通过线程池THREAD_POOL_EXECUTOR去完成的, 前一个线程池的作用只是用来保存了任务及任务的排队。

以下是THREAD_POOL_EXECUTOR的初始化代码,比较简单:

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

2.2 executeOnExecutor方法

@MainThread
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(); //此处在执行任务前,去调用了onPreExecute方法

    mWorker.mParams = params;//把传入进来的参数进去保存
    exec.execute(mFuture);//mFeature就是根据params创建的一个FeatureTask

    return this;
}

首先判断了当前的状态如果不是pending,则表示此任务已经在运行或者是运行完毕,这种情况下直接抛出异常, 也就解释为什么execute方法只能运行一次。

然后用exec这个线程池,也就是上面传入的线程池,去执行mFeature这个任务,这里对mWorker、mParams、mFeature参数解释一下:

  • mWorker:是一个Callable对象,和Runnable比较相似,不过Callable执行后会有返回值,而Runnable没有返回值,都是多线程常用对象;
  • mParams: 我们传入到AsyncTask中的参数;
  • mFeature:是一个FeatureTask对象,也是多线程中常用对象,用来保存mWorker这个Callable对象,并且标注它的执行状态;

通过以上分析,其实在这里 exec.execute(mFuture);任务就处于执行或等待执行了。

我们重写的AsyncTask的doInBackGroud方法呢?怎么没有看见??? 其实都在构造方法里面。

2.3 AsyncTask的构造方法

前面的梳理是从execute开始的,其实在此方法前,我们最先完成的是new AsyncTask对象,所以最开始执行的是构造方法, 只是为了更好理解逻辑,把构造方法放到了这里分析, 源码如下:

/**
 * 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);//1、当此任务被调用时,这里设置一个true的标记
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                result = doInBackground(mParams);//2、调用doInBackground方法
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result); //3、把结果切回主线程
            }
            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);
            }
        }
    };
}

源码注释写到,构造方法需要在主线程调用, 然后在构造方法里面,主要就创建了两个对象mWorker和mFuture, 这两个对象已经在前面介绍了, 不过这里需要注意的是当 mWorker被执行时,实际上就是去调用了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;
}

这里主要是通过getHandler()方法去获取到AsyncTask内部创建的Handler对象,然后发送到主线程的消息队列, getHandler得到的对象源码如下:

private static InternalHandler sHandler;//注释1、 这是一个静态对象

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);//注释2 、回调onProgressUpdate
                break;
        }
    }
}

从源码可以看到,如果是执行完了,则会回调onProgressUpdate方法, 这里,需要非常注意一点,sHandler对象是一个静态对象,因此,一旦在一个进程中完成初始化,就不会再进行初始化了, 另外,根据Handler消息机制的原理,Handler在主线程创建才会和主线程的Looper和MessageQueue进行绑定,才能把消息发往主线程的消息队列,不明白的可以参考另一篇博客–Android消息机制详解:Handler、MessageQueue、Looper 所以可以得出: AsyncTask对象必须在主线程中创建使用

3、为什么AsyncTask要设计为两个线程池?

AsyncTask在Android3.0以后,默认是串行执行的,其实AsyncTask本身也可以提供并行执行的功能,方法就是executeOnExecutor, 只是一般我们不会用到,也不建议这么去使用。如果使用executeOnExecutor方法,则任务的执行会直接交给THREAD_POOL_EXECUTOR这个线程池来执行,而不需要使用默认线程池sDefaultExecutor。 从这里也可以看出,其实Google在这里设计两个线程池,就是为了兼容以前老版本设计和新版本设计,同时保留并行的功能,这就是AsyncTask设计为两个线程池的原因

4、其他知识点

以上基本就完成了对AsyncTask的主要部分的梳理,剩下的onPostExecute和onCancelled两个方法没分析,其实在上述的基础上去看,就会比较简单了,如果感兴趣,可以去了解一下。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值