安卓源码分析——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<Params, Progress, Result>

由原型可见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<URL, Integer, Long> {
        //任务执行前的准备工作
        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<String, Integer, String> {

        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<Params, Progress, Result> execute(Params... params) {
     return executeOnExecutor(sDefaultExecutor, params);
}

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

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

从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<Result> mFuture;

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

再看AsyncTask的构造:

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

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

FutureTask的构造函数:

    public FutureTask(Callable<V> 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<V> 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<Params, Result>() {
        
            //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<Result>(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<Progress>(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的理解,学习其设计思想。如果您觉得有用,请点赞支持一下,您的支持就是我的动力。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容