zoukankan      html  css  js  c++  java
  • AsyncTask源码解析

    AsyncTask 是Google官方提供的供开发者简易使用UI线程的一种解决方案。通过使用AsyncTask,我们可以在后台进行数据操作,然后将结果发布到UI线程,而且
    免了使用Thread和Handler。这篇文章将分析AsyncTask的源码,以探寻其实现原理。

    AsyncTask是Thread和Handler的一个帮助类,它也只能做一些short operations(自己理解,我也不会翻)。如果需要Thread运行很长时间的话还是建议使用 java.util.concurrent 包下的API,例如:Executor、ThreadPoolExecutor、FutureTask。AsyncTask 简单介绍就到这里,对于 AsyncTask 的使用还不熟悉的 移步这里

    线程池配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    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;//空闲线程的超时时间,单位在后面的 THREAD_POOL_EXECUTOR 构造过程中可以看到是 TimeUnit.SECONDS

    private static final ThreadFactory sThreadFactory = new ThreadFactory() {
    private final AtomicInteger mCount = new AtomicInteger(1);//AtomicInteger 在多线程下保证了原子操作

    public Thread (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);

    /**
    * 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();//串行执行的线程池

    可以看到以上代码主要构造了两个线程池:THREAD_POOL_EXECUTOR、SERIAL_EXECUTOR,其中 THREAD_POOL_EXECUTOR 是正常的一个线程池 ,其构造参数前面的 代码块中已经解释过了。对于线程池的构造还不熟悉的同学可以移步这里。另一个线程池SERIAL_EXECUTOR, 从名字上可以看出其是一个串行线程池,对于AsyncTask熟悉的同学应该知道,AsyncTask的默认实现是串行的,那么我们首先看一看 SERIAL_EXECUTOR 是怎么 实现的。

    SerialExecutor 的实现原理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    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() {//将任务存储到 mTasks
    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);//可以看到 SERIAL_EXECUTOR 最终还是将任务交给了 THREAD_POOL_EXECUTOR 执行。
    }
    }
    }

    以上就是SerialExecutor的实现原理,接下来我们看一看构造函数是怎么实现的。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    /**
    * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
    */

    public AsyncTask() {
    mWorker = new WorkerRunnable<Params, Result>() {
    public Result call() throws Exception {
    mTaskInvoked.set(true);

    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
    //noinspection unchecked
    return postResult(doInBackground(mParams));
    }
    };

    mFuture = new FutureTask<Result>(mWorker) {

    protected void done() {
    try {
    postResultIfNotInvoked(get());
    } catch (InterruptedException e) {
    android.util.Log.w(LOG_TAG, e);
    } catch (ExecutionException e) {
    throw new RuntimeException("An error occured while executing doInBackground()",
    e.getCause());
    } catch (CancellationException e) {
    postResultIfNotInvoked(null);
    }
    }
    };
    }

    可以看到以上代码的注释中有一句很重要的话, AsyncTask 必须在UI线程进行构造。那么这是为什么呢?

    默认情况下,Handler会使用当前线程的Looper,如果你的AsyncTask是在子线程创建的,那么很不幸,你的onPreExecute和onPostExecute并非在UI线程执行
    ,而是被Handler post到创建它的那个线程执行;如果你在这两个线程更新了UI,那么直接导致崩溃。这也是大家口口相传的 AsyncTask 必须在主线程
    创建的原因。

    < 大专栏  AsyncTask源码解析p>另外,AsyncTask里面的这个Handler是一个静态变量,也就是说它是在类加载的时候创建的;如果在你的APP进程里面,以前从来没有使用过AsyncTask,然后
    在子线程使用AsyncTask的相关变量,那么导致静态Handler初始化,如果在API 16以下,那么会出现上面同样的问题;这就是AsyncTask必须在主线程初始化
    的原因。

    接下来我们看一看execute方法和executeOnExecutor方法。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46

    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

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

    public enum Status {
    /**
    * Indicates that the task has not been executed yet.
    */

    PENDING,
    /**
    * Indicates that the task is running.
    */

    RUNNING,
    /**
    * Indicates that {@link AsyncTask#onPostExecute} has finished.
    */

    FINISHED,
    }


    注意以上两个方法都必须在UI线程执行。可以看到execute最后也是调用了executeOnExecutor方法,其中的线程池当然是 SERIAL_EXECUTOR 串行线程池,这 也就是为什么说默认情况下 AsyncTask 是串行执行的,为了进行并行计算则需要调用executeOnExecutor方法。可以看到 AsyncTask 有三个状态,如果当前 AsyncTask 已经运行过则会抛出异常,这也就是 AsyncTask 只能执行一次的原因。在这个方法里也会调用onPreExecute() 方法,也就是说这个方法会执行在 UI线程。 最后会调用exec.execute(mFuture); 从构造方法中可以看到最后调用到了 postResultIfNotInvoked 方法,postResultIfNotInvoked 又调用了 postResult 方法,下面我们就看看 postResult 方法。

    消息的处理

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
    new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
    }

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

    private static class InternalHandler extends Handler {
    public InternalHandler() {
    super(Looper.getMainLooper());
    }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})

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

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

    可以看到 postResult 方法也就是通过Handler发送了一条消息,在Handler 中根据消息的不同调用不同的方法,其中会调用到 onProgressUpdate 方法,这也 就是onProgressUpdate 执行在UI线程的原因。至此我们可以看到, 实现AsyncTask 所需要override的方法,都得到了调用。
    最后再放一张流程图。
    image

    使用AsyncTask需要注意的地方

    • AsyncTask的类必须在UI线程加载(从4.1开始系统会帮我们自动完成)
    • AsyncTask对象必须在UI线程创建
    • execute方法必须在UI线程调用
    • 不要在你的程序中去直接调用onPreExecute(), onPostExecute, doInBackground, onProgressUpdate方法
    • 一个AsyncTask对象只能执行一次,即只能调用一次execute方法,否则会报运行时异常
    • AsyncTask不是被设计为处理耗时操作的,耗时上限为几秒钟,如果要做长耗时操作,强烈建议你使用Executor,ThreadPoolExecutor以及FutureTask
      在1.6之前,AsyncTask是串行执行任务的,1.6的时候AsyncTask开始采用线程池里处理并行任务,但是从3.0开始,为了避免AsyncTask所带来的并发错误,
      AsyncTask 又采用一个线程来串行执行任务

    参考文献

  • 相关阅读:
    发现个atan2的正确使用方式
    Forward+ Shading架构
    fatal: unable to connect to gitee.com: gitee.com[0: 180.97.125.228]: errno=Unknown error 解决方案
    HDFS HA(高可用性)集群规划
    如何使用RTP引擎对语音编码进行转码
    关于 Angular 应用 tsconfig.json 中的 target 属性
    浅谈 Orbeon form builder 的权限控制
    关于 Angular 应用 tsconfig.json 中的 lib 属性
    orbeon form 通过 url 的方式同第三方应用集成的开发明细
    orbeon form 的配置介绍
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12326779.html
Copyright © 2011-2022 走看看