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 又采用一个线程来串行执行任务

    参考文献

  • 相关阅读:
    ulimit -a
    nginx check_http_send type=http 查检测不到后端TOM的存活
    通过ip查找能应机器的MAC
    错误状态码URL重定向
    error_page 改变状态码为新的状态码,并显示指定内容
    使用 CefSharp 在 C# App 中嵌入 Chrome 浏览器
    C#客户端嵌入Chrome浏览器的实现
    Task.Run c#启动线程
    C#利用webBrowser怎么去掉网页中不想显示的部分
    InDesign CC 2019安装包+破解
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12326779.html
Copyright © 2011-2022 走看看