AsyncTask知识梳理

上一篇我们讲解了Handler的相关知识,这篇文章就在来对AsyncTask进行讲解。
那么,什么是AsyncTask呢?AsyncTask是一种轻量级的异步线程类,它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程更新UI。简单来说AsyncTask也就是对Handler和线程池的封装。

我们先来看下AsyncTask的一个简单例子:

1        AsyncTask<String, Integer, String> asyncTask = new AsyncTask<String, Integer, String>() {
2            @Override
3            protected String doInBackground(String... params) {
4                return params[0];
5            }

6            @Override
7            protected void onProgressUpdate(Integer... values) {
8                progressBar.setProgress(values[0]*100/1000);
9            }

10            @Override
11            protected void onPostExecute(String s) {
12                Log.e("LHC", "result:"+ s); 
13            }

14            @Override
15            protected void onPreExecute() {
16                //TODO 
17            }
18        };

19        asyncTask.execute("AsyncTask 测试");
  • 1行:生成了一个AsyncTask类对象,而AsyncTask是一个抽象类,具体定义为AsyncTask<Params, Progress, Result>,其中参数Params为在执行任务时,发送给任务的参数类型,也就是方法doInBackground的参数类型;参数Progress为后台执行期间的进度类型,也就是方法onProgressUpdate的参数类型;参数Result为后台执行的结果类型,也就是方法onPostExecute的参数类型。
  • 2~5行:doInBackground(Params... params)方法,在方法中定义后台线程要执行的任务,参数在执行任务也就是调用execute(Params... params)时传入;在方法中可以执行publishProgress(Progress... values)方法,用来更新UI。
  • 6~9行:onProgressUpdate(Progress... values)方法,在UI中用来更新任务进度,由publishProgress(Progress... values)方法内部进行调用。
  • 10~13行:onPostExecute(Result result)方法,在UI线程中用于保存或者显示结果。参数resultdoInBackground执行后的返回结果。
  • 14~17行:onPreExecute()方法,用于做一些准备工作。在doInBackground方法之前调用。
  • 19行:execute(Params... params)方法,执行任务,params就是要传入的参数值。

这样就完成了AsyncTask的简单使用,下面我们就看看源码内部的实现。看第1行代码new AsyncTask<String, Integer, String>()中的代码实现:

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        this((Looper) null);
    }

    /**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    public AsyncTask(@Nullable Looper callbackLooper) {
1       mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

2       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
3                   result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
4                   postResult(result);
                }
                return result;
            }
        };

5       mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
6                   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) {
7                    postResultIfNotInvoked(null);
                }
            }
        };
    }

需要注意的是,代码中的两个构造方法都需要在主线程中运行,看两个方法的注释。代码最终调用到了带参数的AsyncTask(@Nullable Looper callbackLooper)构造函方法,具体分析如下:

  • 1行:根据传入的callbackLooper参数是否为空或者是否为Looper.getMainLooper()也就是主线程的Looper值,来生成一个Handler对象;当为空或者值相等时,调用了getMainHandler()方法;当不为空或者值不相等时,调用了new Handler(callbackLooper)
    getMainHandler()方法,具体代码为:
    private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

    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类对象,传入的是主线程的Looper值。在看InternalHandler类中,在handleMessage中处理了得到的消息。这里的消息有两种类型MESSAGE_POST_RESULTMESSAGE_POST_PROGRESS
MESSAGE_POST_RESULT时,执行了finish方法,代码为:

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

在if语句中判断isCancelled的值,也就是任务是否取消来决定执行取消任务还是调用返回结果的方法onPostExecute。最后将任务状态设置为FINISHED
MESSAGE_POST_PROGRESS时,执行了onProgressUpdate方法,用来更新任务执行的进度。

  • 2,3,4行:生成了一个WorkerRunnable对象mWorker,而这个类是个抽象类并且实现了Callable接口,因此mWorker也就是一个线程类。在call()方法中设置了线程的优先级,并调用了doInBackground(mParams)方法用来执行后台任务;如果出现异常就调用postResult(result)方法。
    postResult(result)方法的具体代码如下:
    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

在这个方法中发送了一条消息类型为MESSAGE_POST_RESULT的消息。

  • 5,6,7行:将上面得到的mWorker作为参数生成了一个FutureTask对象mFuture,而FutureTask类实现了RunnableFuture接口,而RunnableFuture又实现了Runnable, Future接口,因此mFuture也可以看做是一个线程。接下来在FutureTask下的done方法中调用了postResultIfNotInvoked(get())方法。
    postResultIfNotInvoked(get())方法的具体代码如下:
    private void postResultIfNotInvoked(Result result) {
        final boolean wasTaskInvoked = mTaskInvoked.get();
        if (!wasTaskInvoked) {
            postResult(result);
        }
    }

这个方法用来判定是否已经执行过postResult(result)方法。
看第19行代码asyncTask.execute("AsyncTask 测试")中的代码实现:

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

    @MainThread
    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();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

需要注意的是,这两个执行方法也是需要是主线程中执行的。执行代码最终进入了executeOnExecutor执行方法。此方法中现根据任务的mStatus状态值,判定任务是否正在执行或者已经执行过了,如果正在执行或者已经执行过了,就不能在调用执行方法进行执行了,也就是一个AsyncTask任务只能执行一次;如果没有执行过,将任务状态改为正在执行,然后调用onPreExecute()做执行任务前的准备工作,在将传入的参数赋值给mWorker中的mParams变量,最后线程池exec执行线程mFuture。这里的exec也就是sDefaultExecutor,定义如下:

    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

    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继承与Executor,重写了执行方法execute,并且给这个方法加了同步锁,而sDefaultExecutor是SerialExecutor的对象,所以这个线程池也就是一个串行的线程池,任务一个执行完成后另一个才能执行。方法execute中,将生成的新任务加入到mTasks任务队列尾部,而在新任务中执行了r.run()方法,此方法为调用exec.execute(mFuture)中传入的mFuture中的run方法,而在这个run方法中执行了mWorker中的call()方法,这样后台线程也就开始执行了。执行完r.run()之后,又执行了scheduleNext()方法。在这个方法中,通过调用mTasks.poll()方法,删除并获取队列中第一个元素的值,将其赋值mActive,如果值不为空,使用线程池THREAD_POOL_EXECUTOR执行线程mActive;为空则退出。

这样AsyncTask的一整套流程就走完了,但是需要注意的是execute方法默认是串行执行的,也就是一个任务执行完了,另一个才能执行;如果你不想等待一个任务执行完成就去执行另一个任务,该怎么办呢?这时就需要调用executeOnExecutor方法,并传入系统为我们准备好的并行执行的线程池AsyncTask.THREAD_POOL_EXECUTOR,这样就完成了任务的并行执行。简单的例子如下:

        /**
         * 调用“execute”在程序还未执行完时,退出;那么子线程会在后台继续执行,直到执行完成;
         * 当子线程在没有执行完成时,在此调用“execute”方法,此时新建立的子线程并不会执行,只有等上一个子线程执行完成后才能执行。
         * 因为这个方法中的线程池“AsyncTask.SERIAL_EXECUTOR”是串行执行的。
         */
        task.execute("Use Test of AsyncTask.", "1000");
        /**
         * 调用“executeOnExecutor”在程序还未执行完时,退出;子线程在后台继续运行;
         * 当子线程在没有执行完成时,在此调用“executeOnExecutor”方法,新建立的子线程立刻执行。
         * 因为这个方法中的线程池“AsyncTask.THREAD_POOL_EXECUTOR”是并行执行的
         */
        task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "Use Test of AsyncTask.", "1000");

在上面的代码中,也注释了使用并行的一种情况。

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

推荐阅读更多精彩内容

  • 小组成员:赖玉雯 谢婉婷 被评估者:赖玉雯 评估者:谢婉婷 日期:2018-02-01 一:主诉:久坐久站腰会有很...
    50b02b62a966阅读 318评论 0 0
  • 天地万物都造齐了。到第七日,神造物的工已经完毕,就在第七日歇了他一切的工,安息了。神赐福给第七日,定为圣日;因为在...
    OnlywantyouJesu阅读 508评论 0 0
  • 我本是天上一株成了仙的曼珠沙罗,生命的无限,天堂的生活如此乏味。我一直对人间的事有所闻,过腻生活的平淡使我对...
    枫叶落情根绝阅读 282评论 0 0