Android线程之AsyncTask使用与原理分析

 AsyncTask的使用

AsyncTask是一个轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。从实现上来说,AsyncTask封装了Thread和Handler,通过AsyncTask可以更加方便地执行后台任务以及在主线程中访问UI,但是AsyncTask并不适合进行特别耗时的后台任务,对于特别耗时的任务来说,建议使用线程池。
AsyncTask的声明如下:
public abstract class AsyncTask<Params,Progress,Result>
其中,三个泛型类型参数的含义如下
Params:开始异步任务执行时传入的参数类型,即doInBackground()方法中的参数类型;
Progress:异步任务执行过程中,返回下载进度值的类型,即在doInBackground中调用publishProgress()时传入的参数类型;
Result:异步任务执行完成后,返回的结果类型,即doInBackground()方法的返回值类型;
有了这三个参数类型之后,也就控制了这个AsyncTask子类各个阶段的返回类型,如果有不同业务,我们就需要再另写一个AsyncTask
的子类进行处理。
class MAsyncTask extends AsyncTask<Void, Integer, String>{
        private ProgressBar mProgressBar;
        private TextView mTextView;

        public MAsyncTask(ProgressBar mProgressBar, TextView mTextView) {
            this.mProgressBar = mProgressBar;
            this.mTextView = mTextView;
        }

        @Override
        protected void onPreExecute() {
            mTextView.setText("开始执行");
            super.onPreExecute();
        }

        @Override
        protected String doInBackground(Void... params) {
            for(int i = 0; i <= 100; i++){
                publishProgress(i);//此行代码对应下面onProgressUpdate方法
                try {
                    Thread.sleep(100);//耗时操作,如网络请求
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            return "执行完毕";
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            mProgressBar.setProgress(values[0]);
            super.onProgressUpdate(values);
        }

        @Override
        protected void onPostExecute(String s) {
            mTextView.setText(s);
            super.onPostExecute(s);
	}
}

AsyncTask提供了4个核心回调方法,含义如下所示:

(1)onPreExecute() 主线程执行,异步方法执行前调用。
(2)doInBackground(Params...params) 线程池中执行,用于执行异步任务;在方法内部用publishProgress 来更新任务进度。
(3)onProgressUpdate(Progress...value)主线程执行,后台任务进度状态改变时被调用。
(4)onPostExecute(Result result) 主线程执行,异步任务执行之后被调用,其中result参数是后台任务的返回值,即doInBackground的返回值。
AsyncTask对线程和handler进行了封装,那它的封装性体现在哪里呢?其实,AsyncTask的几个回调方法正是这种封装性的体现,使得我们感觉在子线程进行UI更新一样。
执行顺序: onPreExecute->doInBackground->onPostExecute 如果需要取消异步任务,只需调用cancel()方法,该方法会将asynctask类的
mCancelled属性置为true,onPostExecute则不会被调用

然后在主线程中直接调用AsyncTask开启任务线程。

MAsyncTask asyncTask = new MAsyncTask(mTestPB, mTestTV);
 asyncTask.execute();//开始执行

AsyncTask内部实现原理

AsyncTask内部封装了异步任务队列和Handler,下面来分别介绍

1 AsyncTask的内部异步队列

AsyncTask的内部是基于工作队列这种方式的,每次执行execute()就会把当前的任务加入到工作队列中。并且,AsyncTask内部的线程池、工作队列的定义都是static的,而static定义的变量是属于进程范围内的。只有这样,这些static的变量才能交给AsyncTask这个类来管理,而不是AsyncTask的具体子类对象。

 //真正用来执行任务的线程池
   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;
    }
    //定义一个线程池,在线程池中有一个Runnable任务队列,用来存放、顺序执行任务
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    //默认使用的线程池
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    //自定义的一个Handler
    private static InternalHandler sHandler;
可以看到,与线程池有关的都定义为了static类型,其中,SERIAL_EXECUTOR内部就定义了一个任务队列ArrayDeque,当我们调用AsyncTask的execute()时,就会将当前任务加入到该队列中。先来看执行AsyncTask的execute()时的情景:
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
AsyncTask的execute()交给了executeOnExecutor()方法,将将默认的线程池作为参数传进来,进入executeOnExecutor方法中:
public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {//1、这里判断当前AsyncTask是否正在执行或已执行完毕
            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;//2、设置正在执行的状态
        onPreExecute();//3、回调onPreExecute()方法
        mWorker.mParams = params;
        exec.execute(mFuture);//4、放到前面默认构造的线程池中去执行

        return this;
    }
上面第一步,先判断当前AsyncTask是否正在运行或已经执行完毕,如果正在执行或执行完毕再次执行将抛出异常,这也正是我们前面在使用的时候谈到,同一个AsyncTask不能多次进行execute()的原因!到了第三步的时候,先去调用一下onPreExecute()方法,因为executeOnExecutor方法本身就是在UI线程中运行的,所以onPreExecute也会在UI线程中运行。第四步,才会开始讲当前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() {
            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时,实际就是调用的SerialExecutor中的execute方法,在这里面,先创建了一个Runnable对象,然后将这个Runnable对象添加到任务队列mTasks中,在当执行到这个Runnable时调用scheduleNext去队列中取出一个任务,然后交给另一个线程池去真正执行这个任务。

2 与UI线程进行交互-handler

AsyncTask内部通过自定义的static类型的InternalHandler和UI线程进行交互。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;
            }
        }
    }
每当收到子线程发来的和UI线程进行通信的handler请求时,先从Message中拿到子线程发来的结果参数AsyncTaskResult,AsyncTaskResult里面封装了AsyncTask对象和数据信息,如下:
private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }
然后根据不同状态调用不同方法,如果是MESSAGE_POST_RESULT状态,就调用AsyncTask的finish()方法,finish方法中会去判断当前任务是否被cancel,如果没有cancel则开始回调onPostExecute()方法;如果状态是MESSAGE_POST_PROGRESS,则回调onProgressUpdate()方法。


当子线程需要和UI线程进行通信时,就会通过这个handler,往UI线程发送消息。需要通过handler来发送消息,肯定是在子线程异步任务的时候才需要,在AsyncTask中需要handler的地方其实就是两个地方,一个是doInBackground()在运行过程中,需要更新进度值的时候;一个是doInBackground()运行完成后,需要回到到UI线程中的onPostExecute()方法的时候。

对于一:我们在doInBackground()中调用publicProgress()进行进度值的更新,因此在publicProgress()中肯定会有handler的身影,如下:

protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }
对于二:其实就是一个异步任务执行完后后的返回处理,而FutureTask正是处理处理Runnable运行返回结果的。
在1部分的executeOnExecutor方法中第四步,我们在执行execute(mFuture),传入了一个mFuture,mFuture是在初始化AsyncTask的时候进行构建的,如下:
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);
        }
    }
};

在上面的postResultIfNotInvoked()中会通过handler进行消息的发送。

AsyncTask原理总结:
AsyncTask主要是对异步任务和handler的封装,在处理异步任务时,AsyncTask内部使用了两个线程池,一个线程池sDefaultExecutor是用来处理用户提交(执行AsyncTask的execute时)过来的异步任务,这个线程池中有一个Runnable异步任务队列ArrayDequemTasks,把提交过来的异步任务放到这个队列中;另一个线程池THREAD_POOL_EXECUTOR,用来真正执行异步任务的。在处理handler时,自定义了一个InternalHandler,在publicProgress()和doInBackground()运行完成后,会通过这个handler往UI线程发送Message。

3 AsyncTask在使用中的一个特殊情况

我们在使用AsyncTask的时候,一般会在onPreExecute()和onPostExecute()中进行UI的更新,比如等待图片的显示、进度条的显示...当我们一个Activity中正在使用AsyncTask进行文件的下载时,如果此时屏幕发生了旋转,Activity会进行re-onCreate,又会创建一个AsyncTask进行文件的下载,这个正是我们前面将取消任务的时候谈到的第二个现象,我们只需要在onPause()中进行取消cancel()即可。但是这样仅仅是解决了发生等待的情况,因为Activity再次进入了onCreate()方法,还是会进行文件的下载,为了解决这个问题,一种方案是通过判断onCreate(Bundle savedInstanceState)方法参数中的savedInstanceState==null?来判断是哪种情况,只有savedInstanceState==null时才去创建新的AsyncTask。

4 AsyncTask和Handler的比较

AsyncTask:
优点:AsyncTask是一个轻量级的异步任务处理类,轻量级体现在,使用方便、代码简洁上,而且整个异步任务的过程可以通过cancel()进行控制;
缺点:不适用于处理长时间的异步任务,一般这个异步任务的过程最好控制在几秒以内,如果是长时间的异步任务就需要考虑多线程的控制问题;当处理多个异步任务时,UI更新变得困难。

Handler:
优点:代码结构清晰,容易处理多个异步任务;
缺点:当有多个异步任务时,由于要配合Thread或Runnable,代码可能会稍显冗余。

总之,AsyncTask不失为一个非常好用的异步任务处理类,只要不是频繁对大量UI进行更新,可以考虑使用;而Handler在处理大量UI更新时可以考虑使用。











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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值