Android AsyncTask 解析

一 什么是AsnycTask

<p>AsyncTask enables proper and easy use of the UI thread. This class allows you to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.</p>

这句话的意思是,我们在UI线程可以很简单、适用的调用AsyncTask。这个方法可以使我们不用调用Thread+handler的方式,就可以轻松的在后台线程做操作,然后把结果回调给UI线程
<p>AsyncTask is designed to be a helper class around {@link Thread} and {@link Handler}

  • and does not constitute a generic threading framework. AsyncTasks should ideally be
  • used for short operations (a few seconds at the most.) If you need to keep threads
  • running for long periods of time, it is highly recommended you use the various APIs
  • provided by the <code>java.util.concurrent</code> package such as {@link Executor},
  • {@link ThreadPoolExecutor} and {@link FutureTask}.</p>

同时,google也声明了,AsyncTask设计的初衷就是一个用来连接Threa和Handler的帮助类,而不是用于设计一个通用的线程框架,同时,AsnycTask应该用于那些后台操作在极短,最长时间不超过几秒的操作,如果我们需要在后台进行长时间的操作,更推荐使用{@link Executor},{@link ThreadPoolExecutor} and {@link FutureTask}(也即是自定义线程池)去定义自己的后台行为

二 几个重要方法和变量

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

其中execute(Params...params)方法,调用的最终是回归到executeOnExecutor(Executor exec,Params... params)方法中,其中,传入的参数是sDefaultExecutor中,我们回归源码,看到这个sDefaultExecutor是什么?

 /**
 * An {@link Executor} that executes tasks one at a time in serial
 * order.  This serialization is global to a particular process.
 */`    
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;`

好了,也即是说,sDefaultExecutor其实是一个一个串行执行的executor,如果你在使用execute(Params... params)的时候,执行的代码是:

new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            Log.i(MainActivity.this.getClass().getSimpleName(),"execute time "+System.currentTimeMillis());
            return null;
        }

    }.execute();
    new AsyncTask<Void, Void, Void>() {
        @Override
        protected Void doInBackground(Void... params) {
            Log.i(MainActivity.this.getClass().getSimpleName(),"execute time "+System.currentTimeMillis());
            return null;
        }

    }.execute();  

那么,你的执行结果是这样的:

10-22 17:35:33.279 25394-25463/com.example.hao_wu.myapplication I/MainActivity: execute time 1508664933279
10-22 17:35:33.280 25394-25466/com.example.hao_wu.myapplication I/MainActivity: execute time 1508664933280

所以,如果你需要一个并发执行的AsyncTask,那么你需要在execute执行的时候,指定需要调用的executor (AsyncTask.THREAD_POOL_EXECUTOR)

    new AsyncTask<String, Void, Void>() {
        @Override
        protected Void doInBackground(String... params) {
            Log.i(MainActivity.this.getClass().getSimpleName(), "execute time " + System.currentTimeMillis());
            return null;
        }

    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, "");
    new AsyncTask<String, Void, Void>() {
        @Override
        protected Void doInBackground(String... params) {
            Log.i(MainActivity.this.getClass().getSimpleName(),"execute time "+System.currentTimeMillis());
            return null;
        }

    }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,""); 

这个时候,我们来看一下线程的执行情况:

10-22 17:45:49.045 2254-2393/com.example.hao_wu.myapplication I/MainActivity: execute time 1508665549045
10-22 17:45:49.045 2254-2395/com.example.hao_wu.myapplication I/MainActivity: execute time 1508665549045

综上所述,AsnycTask 提供了两个线程池,一个SERIAL_EXECUTOR,用于控制AsyncTask线程的串行执行,也可以说,AsnycTask用这个线程池来实现排队队列;另一个是AsyncTask.THREAD_POOL_EXECUTOR,用于真正的执行线程.

三 言归正传

AsyncTask为我们提供四个核心的回调方法,我们来看一下当我们初始化一个AsnycTask时,会给我们回调什么方法:

    new AsyncTask<Void, Void, Void>() {

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }
        @Override
        protected Void doInBackground(Void... params) {
            return null;
        }

        @Override
        protected void onPostExecute(Void aVoid) {
            super.onPostExecute(aVoid);
        }

        @Override
        protected void onProgressUpdate(Void... values) {
            super.onProgressUpdate(values);
        }
    }.execute();  

上面的回调方法中,我们着重看一下doInbackground()方法,通过翻看源码我们可以看到,

public AsyncTask() {
    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;
        }
    };

    ...
}  

也即是说,当我们在线程开始执行的时候,会回调doInBackground方法给调用方,当方法结束的时候,AsnycTask通过postResult(result)方法,把结果通过handler回调给UI线程

 private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

AsnycTask定义了一个InnerHandler,去接受这个message

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

而后呢,AsnycTask在finish方法中,把线程结束的信息回调给 onPostExecute(result)

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

自此,AsnycTask中源码的调用流程就已经分析完了,以下是几个应该注意的地方:

1 AsnycTask应该在UI线程中被初始化和使用,因为创建Handler对象时需要当前线程的Looper,所以我们为了以后能够通过sHandler将执行环境从后台线程切换到主线程,我们必须使用主线程的Looper,因此必须在主线程中创建sHandler。

2 AsnycTask定义了两个线程池线程数,分别是:

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
// We want at least 2 threads and at most 4 threads in the core pool,
// preferring to have 1 less than the CPU count to avoid saturating
// the CPU with background work
private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;

官方的解释是:
我们至少需要2个线程,最多需要4个线程在核心池中,采用比cpu数少一个的方式,避免后台工作的CPU饱和
另外,定义的两个线程池数量的作用是:
1、如果线程池的当前大小还没有达到基本大小(CORE_POOL_SIZE < MAXIMUM_POOL_SIZE),那么就新增加一个线程处理新提交的任务;
2、如果当前大小已经达到了基本大小,就将新提交的任务提交到阻塞队列排队,等候处理;
3、如果队列容量已达上限,并且当前大小CORE_POOL_SIZE没有达到MAXIMUM_POOL_SIZE,那么就新增线程来处理任务;
4、如果队列已满,并且当前线程数目也已经达到上限,那么意味着线程池的处理能力已经达到了极限,此时需要拒绝新增加的任务

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

推荐阅读更多精彩内容