AsyncTask源码解析

参考资料

鸿洋版AsyncTask
郭霖版AsyncTask
线程池
Android开发艺术探索
Android源码

相关背景知识:

Android消息机制、线程池。

AsyncTask是什么?

(1)AsyncTask是一种轻量级的异步任务类,可以在线程池中执行耗时任务,然后把执行的进度和最终结果传递给主线程并更新UI;AsyncTask封装了Thread和Handler,可以更加方便执行耗时任务,并在主线程中更新UI;
(2)AsyncTask是一个抽象泛型类,提供了Params、Progress和Result三个泛型参数,Params表示参数类型,Progress表示后台任务执行进度的类型;Result表示后台任务结果返回类型;如果不确定传什么参数,那么这三个泛型参数可以用void来代替;

AsyncTask4个核心方法

(1)onPreExecute:在主线程中执行,在异步任务执行之前,此方法可以被调用,一般用于做一些准备工作;
(2)doInBackGround(Params...params),在线程池中执行,用于执行异步任务;params表示异步任务输入参数,在该方法中可以调用publishProgress方法来更新任务进度,该方法会调用到onProgressUpdate中;另外此方法需要将执行结果返回给onPostExcute方法;
(3)onProgressUp0date:在主线程中执行,后台任务执行进度发生改变时此方法被调用;
(4)onPostExcute(Result result):在主线程中执行,result为doInBackgroud返回值;

AsyncTask执行过程

(1)启动方法:new AysncTask().execute();
(2)new AsyncTask()构造方法

    public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            //执行FutureTask run方法会调用到此处
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(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);
                }
            }
        };
    }

(3)调用AsyncTask.execute()方法

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

其中sDefaultExecutor是SerialExecutor实例代码如下

    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();去执行下一个任务;
                        //所以当上一条任务没有执行完,下一个任务是不会执行的;这样AsyncTask中
                        //所有任务都是串行执行的;
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) { //第一次执行时为null;所以会执行scheduleNext();
                //第一次执行时,传入的参数为mFuture,所以执行mFuture的run方法;因为
                第二次再次执行时mActive不为null所以不会再次进入该方法,该方法尽在首日调用时进入;
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            /*任务队列中有任务时,会将任务放到THREAD_POOL_EXECUTOR线程池中去执行;  
            线程池相关内容请参考相关文章*/
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

其中THREAD_POOL_EXECUTOR为:

    //CORE_POOL_SIZE:为核心线程数量,MAXIMUM_POOL_SIZE最大线程数;
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

(4)执行FutureTask run方法:

    public void run() {
        //该方法在线程池中执行;
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            //在AsyncTask构造方法中初始化FutureTask时,会传入mWorker,mWorker是WorkRunable对象,
            WorkRunable实现Callable接口
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    //调用AsyncTask mWorker对象的call方法;
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            ...
        }
    }

(5)AsyncTask执行mWorker call方法:

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                //在此时执行AsyncTask doInBackground方法;
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                执行完毕之后,调用postResult方法;
                return postResult(result);
            }
        };

6.执行AsyncTask postResult方法:

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        /*InternalHandler发送MESSAGE_POST_RESULT消息,下一步会执行
        InternalHandler handleMessage对象;Android消息机制请参考相关文章;*/
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

其中getHandler返回InternalHandler对象,InternalHandler源码如下:

    private static class InternalHandler extends Handler {
        public InternalHandler() {
            //Looper.getMainLooper获取主线程looper,这样
            //handler在收到消息并调用handleMessager时可以切换到主线程;
            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
                    在收到消息时,调用AsyncTask finish方法;*/
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    /*如果在doInBackgound中执行publishProgress,publishProgress会发送
                    MESSAGE_POST_PROGRESS,这样会调用AsyncTask onProgressUpdate方法,该方法可以
                    在UI线程中更新异步任务的进度;*/
                    result.mTask.onProgressUpdate(result.mData);
                    break;
            }
        }
    }

(7)执行AsyncTask finish方法:

  private void finish(Result result) {
      if (isCancelled()) {//如果没有调用cancel方法,该方法返回false;
          onCancelled(result);
      } else {
          //执行onPostExcute方法,这样当前任务执行完毕;然后继续回到SerialExecutor中执行finally
          中的scheduleNext();继续执行下一个任务;
          onPostExecute(result);
      }
      //将mStatus修改为Status.FINISHED,如果两次执行AsyncTask那么执行到executeOnExecutor方法时,会跑出异常;
      mStatus = Status.FINISHED;
  }
其他相关问题:

(1)AsyncTask是否必须在主线程创建并执行?
答:创建:在UI线程和子线程都可以创建AsyncTask;
执行:在主线程中执行AsyncTask是正常流程;所以不会有问题;在子线程中执行AsyncTask:1.由于onPreExecute和启动AsyncTask是在同一个线程,所以只要onPreExecute不操作UI就不会有异常;2.onProgressUpdate:publishProgress会获取InternalHandler,并发送MESSAGE_POST_PROGRESS消息;由于InternalHandler使用的是主线程中的looper,所以在执行Handler.handleMessage()时,已经切换到主线程,所以在子线程执行AsyncTask时,onProgressUpdate是可以操作UI控件的;3.onpostExecute同onProgressUpdate;
结论:在主线程和子线程都可以创建AsyncTask,在子线程(只要onPreExecute不操作UI控件)和主线程
都可以执行AsyncTask;
(2)Android开发艺术探索提到AsyncTask中有两个线程池(SerialExecutor、THEAD_POOL_EXCUTOR),对吗?
答:其中SerialExcutor知识一个线程执行的东西,不是线程池,Android开发艺术探索书写有误;
(3)AsyncTask串行执行还是并行执行?
1.AsyncTask在并发执行多个任务时会发生异常。在3.0以前的系统中还是会以支持多线程并发的方式执行,同一时刻能够运行的线程数为5个,线程池总大小为128。也就是说当我们启动了10个任务时,只有5个任务能够立刻执行,另外的5个任务则需要等待,当有一个任务执行完毕后,第6个任务才会启动,以此类推。而线程池中最大能存放的线程数是128个,当我们尝试去添加第129个任务时,程序就会崩溃。
2,通过源码可以看出现在所有任务被执行时,都是先在SerialExcutor进行排队,执行完上一个任务才会执行下一个任务;所以目前AsyncTask中的任务是串行执行的,通过代码实测,其中排队的任务可以有N多个,尝试20000个任务程序也没有崩溃;
3.如果想并行执行可以executeOnExcutor(AsyncTask.THREAD_POOL_EXECUTOR,new AsyncTask());目的就是跳过SerialExecutor排队流程;
(4)为什么子线程不能更新UI控件?
1.因为UI控件不是线程安全的;
(5)为什么不对UI控件的访问加上锁机制?
1.加上锁之后,访问逻辑变的复杂;
2.锁机制降低UI访问效率;
(6)为什么AsyncTask只能执行一次?
因为执行完成后会将mStatus修改为Status.FINISHED,再次执行到executeOnExecutor方法时,如果mStatus为FINISHED,那么就会抛出异常;

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

推荐阅读更多精彩内容