深入理解AsyncTask

Posted ha_cjy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了深入理解AsyncTask相关的知识,希望对你有一定的参考价值。

众所周知,在android中如果要执行耗时的操作,一般是在子线程中处理,使用new Thread的方法实现是最常见的方法之一。今天,我们要讲的是另外一个,Android提供的异步任务类AsyncTask,底层是使用线程池实现的。

 

一、Android的线程

线程是操作系统的最小执行单位,它的创建和销毁都会消耗一定的系统资源,如果频繁的创建和销毁,显然不是高效的做法,正确的做法是,采用线程池,缓存一定量的线程,通过复用这些线程,避免造成极大的系统开销。

 

二、AsyncTask

这是一个抽象类,使用方便,代码简洁,所以说是一个轻量级的异步类。它可以在线程池中执行后台任务,然后把执行的进度和结果通过Handler传给UI线程进而刷新视图。

该类的声明如下:

public abstract class AsyncTask<Params, Progress, Result>

其中各个参数的含义如下:

Params:开始异步任务执行时传入的参数类型;

Progress:异步任务执行过程中,返回下载进度值的类型;

Result:异步任务执行完成后,返回的结果类型;

如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。

 

三、AsyncTask源码

AsyncTask内部封装了2个线程池:SerialExecutor和THREAD_POOL_EXECUTOR,和1个Handler(IntentHandler)。其中SerialExecutor线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR线程池才真正地执行任务,InternalHandler用于从工作线程切换到主线程。部分源码如下:

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);
            }
        }
    }
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
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;
            }
        }
    }

 

四、注意事项

1、因为执行后的结果要传递到主线程,所以使用Handler进行工作线程和主线程的切换,所以AsyncTask创建实例和execute需要在主线程调用;

2、onPreExecute(),onProgressUpdate(Progress... values),onPostExecute(Result result),onCancelled()方法是在主线程执行的,而doInBackground(Params... params)是在工作线程执行;

3、执行顺序:onPreExecute() --> doInBackground() --> publishProgress() --> onProgressUpdate() --> onPostExecute(),如果不需要执行更新进度则为onPreExecute() --> doInBackground() --> onPostExecute(),其中publishProgress方法在doInBackground方法中调用,会触发onProgressUpdate方法;

4、AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。

5、一个任务实例只能执行一次,如果执行第二次将会抛出异常。

 

五、AsyncTask使用不当的后果

1、生命周期:AsyncTask不与任何组件绑定生命周期,所以在Activity或者Fragment中创建执行AsyncTask时,最好在Activity或Fragment的onDestory()调用 cancel(boolean);

2、内存泄漏:如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露;

3、 结果丢失:屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

 

参考链接:

http://www.jianshu.com/p/817a34a5f200

以上是关于深入理解AsyncTask的主要内容,如果未能解决你的问题,请参考以下文章

教你写Http框架——三个样例带你深入理解AsyncTask

如何在切换片段时停止 AsyncTask?

Asynctask结果显示重新创建片段后

片段中的 Asynctask 未到达 onPostExecute

在 AsyncTask 中将新的 TextView 设置为片段

AsyncTask 和 FragmentManager 的问题