安卓源码分析——AsyncTask

1 AsyncTask基础

1.1 AsyncTask作用

AsyncTask是安卓开发中使用的一种轻量级异步任务类。其作用是在线程池中执行后台任务,并在执行过程中将执行进度传递给主线程,当任务执行完毕后,将最终结果传递给主线程。


1.2 AsyncTask产生背景

安卓系统线程分为主线程和子线程,主线程也叫UI线程。主线程主要负责与用户交互。为了更好的用户体验,保证系统不因主线程的阻塞而产生卡顿,安卓系统要求主线程中不能执行耗时任务。例如:IO操作、网络请求等必须在子线程中完成。AsyncTask就是为了适应这种需要而产生。


1.3 AsyncTask使用场景

AsyncTask是一种轻量级的异步线程,虽然使用AsyncTask可以更加方便的执行后台任务与在主线程中访问UI,但是AsyncTask不适合执行特别耗时的后台任务。具体原因会在后续分析中阐明。


1.4 AsyncTask使用方式

AsyncTask原型:

public abstract class AsyncTask

由原型可见AsyncTask是一个泛型抽象类。
参数说明:

Params 执行后台任务所需参数类型
Progress 后台任务执行进度的类型
Result 后台任务执行完毕后返回结果类型
AsyncTask 核心方法:

AsyncTask提供4个核心方法:
1) protected void onPreExecute()

执行线程 主线程
调用时间 异步任务执行之前
方法作用 异步任务执行前的初始化工作

2)protected Result doInBackground(Params...params)

执行线程 线程池中执行
调用时间 任务开始后到任务结束之前
方法作用 用于执行异步任务

3)protected void onProgressUpdate(Prgress...values)

执行线程 主线程
调用时间 任务开始后到任务结束之前
方法作用 用于更新任务进度

4)protected void onPostExecute(Result result)

执行线程 主线程
调用时间 异步任务执行之后
方法作用 将异步任务的执行结果传递给主线程

了解各个参数以及方法含义后我们来看一段官方给出的AsyncTask示例程序:

    private class DownloadFilesTask extends AsyncTask {
        //任务执行前的准备工作
        protected void onPreExecute(){
        }
        protected Long doInBackground(URL... urls) {
             int count = urls.length;
             long totalSize = 0;
             for (int i = 0; i < count; i++) {
                
                 totalSize += Downloader.downloadFile(urls[i]);
                 
                 //更新下载进度,publishProgress会调用onProgressUpdate
                 publishProgress((int) ((i / (float) count) * 100));
                 // Escape early if cancel() is called
                 if (isCancelled()) break;
             }
              return totalSize;
          }
     
         //主线程中调用,主要用于更新后台任务进度
         protected void onProgressUpdate(Integer... progress) {
              setProgressPercent(progress[0]);
          }
     
          //任务执行完毕
          protected void onPostExecute(Long result) {
              showDialog("Downloaded " + result + " bytes");
          }
      }

DownloadFilesTask类模拟文件下载过程。传入的参数Params类型为URL(文件地址),后台任务进程参数Progress类型为Integer(下载进度),后台任务返回结果参数Result类型为Long(总文件大小)。


1.5 AsyncTask使用注意

通过简单的官方示例程序学习了AsyncTask的使用方法,那么使用AsyncTask需要注意以下几个条件:
1)AsyncTask类必须在主线程加载
2)AsyncTask对象必须在主线程创建
3)execute方法必须在主线程调用
4)不要在程序中直接调用AsyncTask提供的4个核心方法
5)一个AsyncTask对象只能执行一次,即只能调用一次execute


1.6 小结

通过本节的介绍,对AsyncTask有了初步的认识,第二部分将从AsyncTask源码出发,分析AsyncTask的工作原理以及回答本节中留下的问题。


2 源码分析

在第1小节中主要介绍了AsyncTask的基本知识,本节将从源码出发,深层次分析AsyncTask的工作原理。


2.1 AsyncTask对象创建

使用AsyncTask时,一般根据具体的任务继承AsyncTask类,例如:

private class MyAsyncTask extends AsyncTask {

        protected String doInBackground(String... args1) {
        
            Log.i(TAG, "doInBackground in:" + args1[0]);
            int times = 10;
            for (int i = 0; i < times; i++) {
                publishProgress(i);//提交之后,会执行onProcessUpdate方法
            }
            Log.i(TAG, "doInBackground out");
            return "over";
        }

        /**
         * 在调用cancel方法后会执行到这里
         */
        protected void onCancelled() {
            Log.i(TAG, "onCancelled");
        }

        /**
         * 在doInbackground之后执行
         */
        protected void onPostExecute(String args3) {
            Log.i(TAG, "onPostExecute:" + args3);
        }

        /**
         * 在doInBackground之前执行
         */
        @Override
        protected void onPreExecute() {
            Log.i(TAG, "onPreExecute");
        }

        /**       
         * @param args2
         */
        @Override
        protected void onProgressUpdate(Integer... args2) {
            Log.i(TAG, "onProgressUpdate:" + args2[0]);
        }
    }

MyAsyncTask对象的创建必须在主线程中,使用方式为:

new MyAsyncTask().execute("AsyncTask Test");

2.2 execute方法

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

execute方法调用了executeOnExecutor方法并传递参数sDefaultExecutor和params。
再看executeOnExecutor方法:

public final AsyncTask 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
        mWorker.mParams = params;
        
        //调用线程池,执行任务
        exec.execute(mFuture);

        return this;
}

executeOnExecutor方法首先判断状态,若处于可执行态,则将状态置为RUNNING。然后调用了onPreExecute方法,交给用户进行执行任务前的准备工作。核心部分在于 exec.execute(mFuture)。exec即sDefaultExecutor。


2.3 sDefaultExecutor串行线程池

查看sDefaultExecutor定义:

public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

sDefaultExecutor是一个串行线程池,作用在于任务的排队执行。其源码如下:

    private static class SerialExecutor implements Executor {
    
        final ArrayDeque mTasks = new ArrayDeque();
        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的源码可以看出,mFuture是插入到mTasks任务队列的对象。当mTasks中没有正在活动的AsyncTask任务,则调用scheduleNext方法执行下一个任务。若一个AsyncTask任务执行完毕,则继续执行下一个AsyncTask任务,直至所有任务执行完毕。通过分析可以发现真正去执行后台任务的是线程池THREAD_POOL_EXECUTOR。


2.4 线程池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.5 mFuture并发执行对象

线程池THREAD_POOL_EXECUTOR中执行的Runnable对象为mFuture。

mFuture的定义:

private final FutureTask mFuture;

mFuture为线程池执行的真正任务,mFuture的执行过程过程是怎样的呢?

再看AsyncTask的构造:

    public AsyncTask(@Nullable Looper callbackLooper) {
    
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);
        //创建mworker对象
        mWorker = new WorkerRunnable() {
            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对象
        mFuture = new FutureTask(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);
                }
            }
        };
    }

通过AsyncTask的构造中可以看到mWorker为FutureTask的构造函数参数,则FutureTask对象中持有mWorker的引用。

FutureTask的构造函数:

    public FutureTask(Callable callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;     
    }

FutureTask的run方法:

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

FutureTask的run方法中调用了 result = c.call(); 即调用了mWorker的call方法。


2.6 mWorker对象

mWorker的call方法:

        mWorker = new WorkerRunnable() {
        
            //call方法
            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;
            }
        };

通过代码可以发现,最终在线程池中执行的是mWorker的call方法,call方法中调用了doInBackground方法,因此可以看出doInBackground方法是在线程池中调用的。
当任务执行完毕后则调用postResult方法:

    //任务执行完后调用方法
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult(this, result));
        message.sendToTarget();
        return result;
    }
    

postResult方法发送MESSAGE_POST_RESULT消息和result。


2.7 InternalHandler接收处理消息

InternalHandler代码:

    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

        @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接收到MESSAGE_POST_RESULT时调用result.mTask.finish(result.mData[0]);


2.8 finish方法:

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

finish方法中若任务没有取消则调用onPostExecute方法发送结果,若任务取消则调用onCancelled方法。finish方法是在主线程中执行的。


2.9 onProgressUpdate

通过上述流程已经顺序找到了onPreExecutedoInBackgroundonPostExecute方法,那么onProgressUpdate是如何执行的呢?
首先查看 publishProgress方法:

    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult(this, values)).sendToTarget();
        }
    }

MyAsyncTask示例中在doInBackground中调用publishProgress方法,publishProgress方法发送MESSAGE_POST_PROGRESS消息和进度values,InternalHandler在接收到MESSAGE_POST_PROGRESS消息中调用onProgressUpdate方法。因此onProgressUpdate也是在主线程中调用。


2.10 小结

通过上述一步步的源码分析过程,已经掌握了AsyncTask任务的执行过程。AsyncTask中有两个线程池串行线程池sDefaultExecutor和线程池THREAD_POOL_EXECUTOR。sDefaultExecutor用于任务的排队,THREAD_POOL_EXECUTOR真正的执行任务。线程的切换使用Handler(InternalHandler)实现。


3 问题解答

1)为什么AsyncTask在主线程创建执行?
因为AsyncTask需要在主线程创建InternalHandler,以便onProgressUpdateonPostExecuteonCancelled 可以正常更新UI。
2)为什么AsyncTask不适合特别耗时任务?
AsyncTask实际上是一个线程池。如果有线程长时间占用,且没有空闲,则其他线程只能处于等待状态,会造成阻塞。
3)AsyncTask内存泄漏问题
如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。


总结

通过对AsyncTask源码分析,学习了AsyncTask的工作原理,以及在使用AsyncTask的注意事项。希望通过本篇文章的学习可以加深对于AsyncTask的理解,学习其设计思想。如果您觉得有用,请点赞支持一下,您的支持就是我的动力。

你可能感兴趣的:(安卓源码分析——AsyncTask)