AsyncTask解析篇

流程解读
  • 自定义的继承类:因为AsyncTask为抽象类,无法直接创建,必须继承它实现一个子类。
a. 常见的方法
onPreExecute():在执行任务前调用,位于UI线程内
doInBackground():执行任务过程,位于非UI线程内
publishProgress():发布任务进度,一般在doInBackground()中调用,将数据传递给调onPregressUpdate()方法
onPregressUpdate():更新进度条,位于UI线程
onPostExecute():doInBackgound()方法完成后调用,位于UI线程
onCancelled():取消任务,位于UI线程
b. 定义类型
class MyTask extends AsyncTask<Params, Progress, Result>
Params为传入的数据,比如url;因此,通常设置为String类型;
Progress为更新的进度,通常为Integer类型;
Result为返回的结果,如果我们希望返回一个文件,那么其对应类型为File;
  • 创建自定义的MyTask类
MyTask task = new MyTask(...);

创建该类,实际上就会调用AsyncTask的构造函数,如下:

//创建一个异步任务,注意这里必须在UI线程中创建
public AsyncTask(){
    //定义任务执行过程,注意这里还没开始执行任务,只是定义任务的执行过程
    mWorker = new WorkerRunnable<Params, Result>() {
        @Override
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //交给子类去实现,获取到结果
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr){
                mCancelled.set(true);
                throw tr;
            } finally {
                //最后提交结果
                postResult(result);
            }
            return result;
        }
    };

    //实际上,这里用FutureTask包装Callable的目的是为之后提交给线程池执行,因为线程池并不接收Callable类型
    mFuture = new FutureTask<Result>(mWorker){
        @Override
        protected void done() {
            try {
                //若任务并没有执行过,则需要调用get()方法获取到结果,然后再提交结果
                postResultIfNotInvoked(get());
            } catch (InterruptedException e){
                android.util.Log.w(LOG_TAG, e);
            } catch (ExecutionException e){
                throw new RuntimeException("An error occurred while executing doInBackgound", e.getCause());
            } catch (CancellationException e){
                postResultIfNotInvoked(null);
            }
        }
    };
}

在AsyncTask构造函数内,初始化了两个mWorkermFuture两个变量。我们容易看到其中mFutrue实际上就是一个包装mWorker的FutrueTask实例。

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result>{
        Params[] mParams;
}

如上,实际上WorkerRunnable类型即为Callable类型。

  • 执行MyTask实例
mTask.execute(url);

其对应调用AsyncTask中的execute方法,如下:

//外部调用方法,执行任务
public final AsyncTask<Params, Progress, Result> execute(Params... params){
    return executeOnExecutor(sDefaultExecutor, params);
}

//真正执行任务的地方
private 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 benn executed.");
        }
    }
    //标识为正在执行
    mStatus = Status.RUNNING;
    //执行前调用
    onPreExecute();
    mWorker.mParams = params;
    //执行任务!!!!
    exec.execute(mFuture);
    return this;
}

首先我们看到executeOnExecutor中的onPreExecute()方法,这是任务前执行的方法,然后执行exec.execute(mFuture),我们回到mFuture的定义,我们知道mFuture只是mWorker的一层包装,目的是为了交给线程池执行。而mWorkder中会执行result = doInBackground(mParams);,这个是交给子类去实现,获取到结果。最后再分发结果postResult(result);

//获取到Handler对象
private static Handler getHandler(){
    synchronized (AsyncTask.class){
        if (sHandler == null){
            sHandler = new InternalHandler();
        }
        return sHandler;
    }
}

//提交结果
private Result postResult(Result result){
    //创建消息
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
    //发送消息
    message.sendToTarget();
    return result;
}

我们可以看到消息将对应的结果通过一个InternalHandler类型来发送

//定义一个UI线程上的Handler对象
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:
                result.mTask.finish(result.mData[0]);
            break;
            case MESSAGE_POST_PROGRESS:
                result.mTask.onProgressUpdate(result.mData);
                break;
        }
    }
}

如上,该类型为UI线程上的一个Handler对象,保证发送的消息可以在UI线程中接收,我们先看到其MESSAGE_POST_RESULT类型,调用result.mTask.finish(result.mData[0]);方法

//任务执行完毕,提交结果,并标识
private void finish(Result result){
    if (isCancelled()){
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

我们可以看到在finish方法中,调用了onPostExecute(result)方法将结果发送出去。这样一个完整的流程就结束了。

当然,还有其更新进度方面,我们回到publishProgress()方法

//更新进度,并发送进度消息
protected final void publishProgress(Progress... values){
    if (!isCancelled()){
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}

回到InternalHandler中,我们可以看到其对应的消息类型,会调用onProgressUpdate()方法

细节解析
  • AsyncTask默认是使用串行执行的方式
//线程执行器,默认为串行执行
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

如何实现串行执行?
添加任务时,将下一个任务添加到上一个任务完成后执行

//实现串行执行
private static class SerialExecutor implements Executor{
    //双向队列,存储任务
    final ArrayDeque<Runnable> mTasks = new ArrayDeque<>();
    //当前正在执行的任务
    Runnable mActive;

    @Override
    public void execute(final Runnable r) {
        //添加到队尾
        //注意:a. 在这里不是直接传入r,而是重新定义一个runnable,目的在于当前任务执行完后,再从任务队列中获取到下一个,以保证串行
        //     b. 在这里只是重新定义runnable,并没有开始真正执行!!!
        mTasks.offer(new Runnable() {
            @Override
            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);
        }
    }
}
  • 定义线程池的方法

亮点在于:静态化构造线程池,这样当构建对象时,会初始化线程池

//获取当前CPU核心数
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
//定义线程池核心容量
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;
//过剩的空闲线程的存活时间
private static final int KEEP_ALIVE_SECONDS = 30;

//自定义工厂方法,标记线程,以便管理
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);

    @Override
    public Thread newThread(Runnable r) {
        return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
    }
};
//任务队列
private static final BlockingQueue<Runnable> sPoolWorkQueue = new LinkedBlockingQueue<>(128);
//线程池
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;
}
  • 关于不定参数的使用
private static class AsyncTaskResult<Data>{
    final AsyncTask mTask;
    final Data[] mData;

    public AsyncTaskResult(AsyncTask mTask, Data... mData) {
        this.mTask = mTask;
        this.mData = mData;
    }
}
使用不定参数的目的在于在构造这样的类时,传入的Data数据类型可变:
private Result postResult(Result result){
    //创建消息
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
            new AsyncTaskResult<Result>(this, result));
}

protected final void publishProgress(Progress... values){
    if (!isCancelled()){
        getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }
}
如上,我们可以看到构造AsyncTaskResult对象时,既可以传入单个对象,也可以传入数组对象。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念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

推荐阅读更多精彩内容