AsyncTask机制详解

  AsyncTask是Android提供的一个轻量级异步任务机制,使用AsyncTask可以方便的执行异步任务,并将结果更新到main thread。AsyncTask中是通过Handler机制来让work thread和main thread通信的。如果你对Handler还不了解的话,可以通过我的这篇博客来了解Android的Handler机制。Android 异步消息处理机制

  在这篇文章中我们将了解AsyncTask的基本用法以及从源码的角度来分析AsyncTask机制,首先我们来了解下开发过程中AsyncTask的使用

AsyncTask的基本用法

AsyncTask类的基本方法:

public abstract class AsyncTask<Params, Progress, Result> {
/**
     * Override this method to perform a computation on a background thread. The
     * specified parameters are the parameters passed to {@link #execute}
     * by the caller of this task.
     *
     * This method can call {@link #publishProgress} to publish updates
     * on the UI thread.
     *
     * @param params The parameters of the task.
     *
     * @return A result, defined by the subclass of this task.
     *
     * @see #onPreExecute()
     * @see #onPostExecute
     * @see #publishProgress
     */
    @WorkerThread
    protected abstract Result doInBackground(Params... params);

    /**
     * Runs on the UI thread before {@link #doInBackground}.
     *
     * @see #onPostExecute
     * @see #doInBackground
     */
    @MainThread
    protected void onPreExecute() {
    }

    /**
     * <p>Runs on the UI thread after {@link #doInBackground}. The
     * specified result is the value returned by {@link #doInBackground}.</p>
     *
     * <p>This method won‘t be invoked if the task was cancelled.</p>
     *
     * @param result The result of the operation computed by {@link #doInBackground}.
     *
     * @see #onPreExecute
     * @see #doInBackground
     * @see #onCancelled(Object)
     */
    @SuppressWarnings({"UnusedDeclaration"})
    @MainThread
    protected void onPostExecute(Result result) {
    }

    /**
     * Runs on the UI thread after {@link #publishProgress} is invoked.
     * The specified values are the values passed to {@link #publishProgress}.
     *
     * @param values The values indicating progress.
     *
     * @see #publishProgress
     * @see #doInBackground
     */
    @SuppressWarnings({"UnusedDeclaration"})
    @MainThread
    protected void onProgressUpdate(Progress... values) {
    }
}

  从上面AsyncTask的代码可以看到,AsyncTask是一个抽象类,我们如果想要使用的话必须先创建它的子类,并实现它的抽象方法doInBackground,根据功能需要我们还可以重写onPreExecute、onPostExecute、onProgressUpdate。从注释我们就可以知道onPreExecute、onPostExecute、onProgressUpdate这几个方法是运行在main thread,doInBackground是运行在workthread中的。我们把耗时的操作放在doInBackground进行,当doInBackground执行完之后会把结果返回给onPostExecute,我们可以在onPostExecute做一些更新UI的操作。onPreExecute在doInBackground之前执行,用于执行准备工作,onProgressUpdate用来更新后台任务的执行进度。

  AsyncTask有三个泛型参数,分别是Params, Progress, Result。Params是指后台任务运行的参数,用于在doInBackground使用,Progress是用来指示后台任务执行进度的单位,Result是后台任务的返回结果,在doInBackground方法中返回,交给onPostExecute处理。这些参数不需要时可以用Void代替。

  在本文中我们自定义一个简单的AsyncTask类,代码定义如下:

public class TestAsyncTask extends AsyncTask<Void, Integer, Void> {
    private String taskName;

    public TestAsyncTask(String name) {
        super();
        taskName = name;
    }

    @Override
    protected void onPreExecute() {
        super.onPreExecute();
    }

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

    @Override
    protected void onPostExecute(Void aVoid) {
        super.onPostExecute(aVoid);
        Log.v("stone", "task = " + taskName + " onPostExecute in " + Thread.currentThread().toString());
    }

    @Override
    protected Void doInBackground(Void... params) {
        Log.v("stone", "task = " + taskName + " doInBackground in " + Thread.currentThread().toString());
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            e.toString();
        }
        return null;
    }

}

//AsyncTask的两种启动方式
new TestAsyncTask("task_" + i).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
new TestAsyncTask("tasktesk_" + i).execute();

  到目前为止我我们已经大致了解了AsyncTask的使用,以及启动后台任务的两种方式,execute和executeOnExecutor。在executeOnExecutor中我们使用了AsyncTask自定义的THREAD_POOL_EXECUTOR,它是一个ThreadPoolExecutor。这两种后台任务的启动方式有什么区别呢?一个应用中最多可以new多个AsyncTask呢?多个任务是并行处理还是串行处理呢?最多同时有多少个任务在处理呢?下面我们将从源码的角度来回答上面的问题,由于AsyncTask从出现到现在已经有了多次改动,下面我的分析将是基于API 23的源码来进行的,请各位注意不同API版本的AsyncTask实现是有差异的。

AsyncTask机制详解

  通过上文我们已经知道了AsyncTask的简单用法,下面我将从源码的角度来分析,一个AsyncTask从创建到执行的过程。首先一个任何AsyncTask子类的创建都会调用AsyncTask的默认构造函数AsyncTask(),下面我们来看一下这个方法的定义:

     public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            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);
                }
            }
        };
    }

  代码很简单,初始化了mWorker 和mFuture 这两个对象,我们暂时先不管立面的具体实现,只要知道这个mFuture 会交给Executor去执行。现在AsyncTask对象已经创建好了,我们来看AsyncTask的执行过程。我们先来分析execute()方法,该方法的定义如下:

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

  execute的代码实际上直接调用了executeOnExecutor方法,并且传入了一个叫做sDefaultExecutor的Executor对象。接着我们来看下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();

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

        return this;
    }

  在executeOnExecutor中,我们先调用了onPreExecute()方法,这就解释了为什么onPreExecute方法是最新被调用的。我们把运行参数params赋给mWorker对象,mWorker实现了callable接口。把mFuture交给我们传入的Executor来执行。对面我们前文提到了AsyncTask的两种启动方式,他们执行方式的不同是因为传入的Executor不同导致的。我们前面提到过execute方法传给executeOnExecutor的是一个sDefaultExecutor对象,通过源码来看一下sDefaultExecutor是什么:

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

  我们看到sDefaultExecutor 被赋值为SERIAL_EXECUTOR,我们来看一下SERIAL_EXECUTOR是如何定义的:

  

    /**
     * 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();

  SERIAL_EXECUTOR 是一个SerialExecutor对象,我们来看一下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();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

  到这里我们可以知道,通过execute启动AsyncTask任务,会把任务一个一个添加到mTasks中,在执行完一个任务后才会去执行下一个任务,mTasks的大小没有限制,所以理论上通过execute启动AsyncTask任务这种方式可以创建无数个task,并且所有的task是串行执行的。这个runnable参数就是我们在AsyncTask构造函数中初始化的mFuture。我们接着再来看一下构造函数的源码:

  

     public AsyncTask() {
        mWorker = new WorkerRunnable<Params, Result>() {
            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);
                }
            }
        };
    }

  我们看到在初始化mWorker 对象时,在它的call方法里面调用了doInBackground方法,执行完doInBackground之后会调用postResult(result)来传递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消息,并将消息发送给了target handler。现在我们就要来看看这个Handler对象是什么,就知道是把消息发送到了哪个线程中。我们来看一下getHandler()方法的实现:

  

    private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

  看到这里,我们知道这个handler是一个InternalHandler对象,我们接着来看一下InternalHandler的源码实现:

  

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

  看到这里已经很清楚,这个handler是绑定了主线程loop的handler,所以接下来的工作就从工作线程切换到主线程中去执行。在handleMessage中可以看到MESSAGE_POST_RESULT会导致调用result.mTask.finish(result.mData[0])。这个就是AsyncTask的finish方法,我们来看一下finish方法的实现:

  

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

  finish方法里面会做判断,如果 isCancelled()返回的是true,就会去执行onCancelled,否则就会调用onPostExecute方法。至此我们就走完了execute方式的启动流程。通过以上的分析,我们可以知道这种方式下AsyncTask的对象可以创建任意多个并执行,且是顺序执行的。

  对于executeOnExecutor启动方式,他的执行方式和传入的Executor相关,每个任务的执行逻辑和execute启动方式是一样的。executeOnExecutor方式中我们传入的参数是AsyncTask.THREAD_POOL_EXECUTOR,这是AsyncTask默认帮我们配置的Executor。我们来具体看下这个Executor的配置情况:

  

private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
    private static final int KEEP_ALIVE = 1;

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) {
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        }
    };

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

  这个ThreadPoolExecutor配置如下:核心线程数为CPU核个数+1,最大线程数是2倍的CPU核个数+1,任务排队队列大小为128,所以假设我们的手机CPU核的个数为8,在核心线程数为9,最大线程数为17。所以根据ThreadPoolExecutor的特点我们可以知道,最大的任务数为128+17,超过这个数量就会抛出RejectedExecutionException。在排队队列未满之前,最多有9个线程在运行,当排队队列满了之后,最多有17个线程在运行。

时间: 2024-10-14 09:33:37

AsyncTask机制详解的相关文章

Android应用AsyncTask处理机制详解及源码分析

[工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果] 1 背景 Android异步处理机制一直都是Android的一个核心,也是应用工程师面试的一个知识点.前面我们分析了Handler异步机制原理(不了解的可以阅读我的<Android异步消息处理机制详解及源码分析>文章),这里继续分析Android的另一个异步机制AsyncTask的原理. 当使用线程和Handler组合实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比

Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

1 背景 还记得前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事件疑惑吗?当时说了,在那一篇咱们只讨论View的触摸事件派发机制,这个疑惑留在了这一篇解释,也就是ViewGroup的事件派发机制. PS:阅读本篇前建议先查看前一篇<Android触摸屏事件派发机制详解与源码分析一(View篇)>,这一篇承接上一篇. 关于View与ViewGroup的区别在前一篇的A

【Hibernate步步为营】--锁机制详解

上篇文章详细讨论了hql的各种查询方法,在讨论过程中写了代码示例,hql的查询方法类似于sql,查询的方法比较简单,有sql基础的开发人员在使用hql时就会变得相当的简单.Hibernate在操作数据库的同时也提供了对数据库操作的限制方法,这种方法被称为锁机制,Hibernate提供的锁分为两种一种是乐观锁,另外一种是悲观锁.通过使用锁能够控制数据库的并发性操作,限制用户对数据库的并发性的操作. 一.锁简介 锁能控制数据库的并发操作,通过使用锁来控制数据库的并发操作,Hibernate提供了两种

浏览器缓存机制详解

对于浏览器缓存,相信很多开发者对它真的是又爱又恨.一方面极大地提升了用户体验,而另一方面有时会因为读取了缓存而展示了"错误"的东西,而在开发过程中千方百计地想把缓存禁掉.那么浏览器缓存究竟是个什么样的神奇玩意呢? 什么是浏览器缓存: 简单来说,浏览器缓存就是把一个已经请求过的Web资源(如html页面,图片,js,数据等)拷贝一份副本储存在浏览器中.缓存会根据进来的请求保存输出内容的副本.当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是

Android触摸屏事件派发机制详解与源码分析

请看下面三篇博客,思路还是蛮清晰的,不过还是没写自定义控件系列哥们的思路清晰: Android触摸屏事件派发机制详解与源码分析一(View篇) http://blog.csdn.net/yanbober/article/details/45887547 Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇) http://blog.csdn.net/yanbober/article/details/45912661 Android触摸屏事件派发机制详解与源码分析三(Activi

SpringMVC视图机制详解[附带源码分析]

目录 前言 重要接口和类介绍 源码分析 编码自定义的ViewResolver 总结 参考资料 前言 SpringMVC是目前主流的Web MVC框架之一. 如果有同学对它不熟悉,那么请参考它的入门blog:http://www.cnblogs.com/fangjian0423/p/springMVC-introduction.html 本文将分析SpringMVC的视图这部分内容,让读者了解SpringMVC视图的设计原理. 重要接口和类介绍 1. View接口 视图基础接口,它的各种实现类是无

Shiro的Filter机制详解---源码分析

Shiro的Filter机制详解 首先从spring-shiro.xml的filter配置说起,先回答两个问题: 1, 为什么相同url规则,后面定义的会覆盖前面定义的(执行的时候只执行最后一个). 2, 为什么两个url规则都可以匹配同一个url,只执行第一个呢. 下面分别从这两个问题入手,最终阅读源码得到解答. 问题一解答 相同url但定义在不同的行,后面覆盖前面 如 /usr/login.do=test3 /usr/login.do=test1,test2 不会执行test3的filter

Android ViewGroup触摸屏事件派发机制详解与源码分析

PS一句:最终还是选择CSDN来整理发表这几年的知识点,该文章平行迁移到CSDN.因为CSDN也支持MarkDown语法了,牛逼啊! [工匠若水 http://blog.csdn.net/yanbober] 该篇承接上一篇<Android View触摸屏事件派发机制详解与源码分析>,阅读本篇之前建议先阅读. 1 背景 还记得前一篇<Android View触摸屏事件派发机制详解与源码分析>中关于透过源码继续进阶实例验证模块中存在的点击Button却触发了LinearLayout的事

Android Touch事件传递机制详解 上

尊重原创:http://blog.csdn.net/yuanzeyao/article/details/37961997 最近总是遇到关于Android Touch事件的问题,如:滑动冲突的问题,以前也花时间学习过Android Touch事件的传递机制,可以每次用起来的时候总是忘记了,索性自己总结一下写篇文章避免以后忘记了,其实网上关于Touch事件的传递的文章真的很多,但是很少有系统性的,都是写了一个简单的demo运行了一下,对于我们了解Android Touch事件基本上没有任何帮助. 今