• 02、Android进阶AsyncTask原理解析


    AsyncTask原理

    Android提供了 AsyncTask,它使得异步任务实现起来更加简单。

    public abstract class AsyncTask<Params, Progress, Result>{
        ......
    }
    

    AsyncTask是一个抽象的泛型类,它有3个泛型参数,分别为Params、Progress和Result。

    Params为 参数类型

    Progress为后台任务执行进度的类型

    Result为返回结果的类型。

    如果不需要某个参数,可以将其设置为Void类型。

    AsyncTask中有4个核心方法,如下所示:

    (1)onPreExecute():在主线程中执行。一般在任务执行前做准备工作,比如对 UI 做一些标记。

    (2)doInBackground(Params...params):在线程池中执行。在 onPreExecute方法执行后运行,用来执

    行较为耗时的操作。在执行过程中可以调用publishProgress(Progress...values)来更新进度信息。

    (3)onProgressUpdate(Progress...values):在主线程中执行。当调用 publishProgress(Progress...values)时,此方法会将进度更新到UI组件上。

    (4)onPostExecute(Result result):在主线程中执行。当后台任务执行完成后,它会被执行。 doInBackground方法得到的结果就是返回的result的值。此方法一般做任务执行后的收尾工作,比如更新UI和数据。

    源码分析

    AsyncTask在3.0版本之前和3.0 及以后版本有着较大的改动,

    Android 3.0版本之前的AsyncTask

    下面是Android 2.3.7版本的AsyncTask的部分源码。

    public abstract class AsyncTask<Params, Progress, Result> {
        private static final String LOG_TAG = "AsyncTask";
        // 核心线程数
        private static final int CORE_POOL_SIZE = 5;
        // 线程池允许创建的最大线程数
        private static final int MAXIMUM_POOL_SIZE = 128;
        // 非核心线程空闲等待新任务的最长时间
        private static final int KEEP_ALIVE = 1;
    
        private static final BlockingQueue<Runnable> sWorkQueue =
                new LinkedBlockingQueue<Runnable>(10);
    
        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 ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
                MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);
        ......
    }    
    

    ThreadPoolExecutor,它的核心线程数是5个,线程池允许创建的最大线程数为 128,非核心线程空闲等待新任务的最长时间为 1s。采用的阻塞队列是LinkedBlockingQueue,它的容量为10。

    3.0版之前的AsyncTask有一个缺点,就是线程池最大的线程数为128,加上阻塞队列的10个任务,所以 AsyncTask最多能同时容纳138个任务,当提交第139个任务时就会执行饱和策略,默认抛出 RejectedExecutionException异常。

    Android 7.0版本的AsyncTask

    Android 7.0版本的AsyncTask的代码如下所示:

    public abstract class AsyncTask<Params, Progress, Result> {
        private static final String LOG_TAG = "AsyncTask";
        // 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);
    
            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;
    
        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;
        }
    }    
    

    ThreadPoolExecutor,它的核心线程数最少有2个,最多是4个。线程池允许创建的最大线程数是根据CPU数量来决定的。阻塞队列仍旧是LinkedBlockingQueue,容量为128。

    1. 首先来看AsyncTask的构造方法,代码如下所示
    public AsyncTask() {
        // 实现了Callable接口,并实现call方法
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
    
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                // 在call中调用doInBackground(mParams)来处理任务并得到结果
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                // 将结果投递出去
                return postResult(result);
            }
        };
    	// FutureTask是一个可管理的异步任务,它实现了Runnable和Futrue这两个接口
        // 所以,FutureTask可以包装 Runnable和Callable,并提供给Executor执行
        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);
                }
            }
        };
    }
    

    WorkerRunnable作为参数传递给了FutureTask,这两个变量会暂时保存在内存中,稍后会用到它们。

    当要执行 AsyncTask 时,需要调用它的 execute方法,代码如下所示:

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

    execute方法又调用了executeOnExecutor方法,代码如下所示:

    @MainThread
    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();
        // 从前面我们知道WorkerRunnable作为参数传递给了FutureTask
        mWorker.mParams = params;
        // 将mFuture也就是前面讲到的FutureTask传进去
        exec.execute(mFuture);
        return this;
    }
    

    这里exec是传进来的参数sDefaultExecutor,它是一个串行的线程池 SerialExecutor,其代码如下所示:

    class SerialExecutor implements Executor {
        final Queue<Runnable> tasks = new ArrayDeque<>();
        final Executor executor;
        Runnable active;
        SerialExecutor(Executor executor) {
            this.executor = executor;
        }
        public synchronized void execute(final Runnable r) {
            // 将FutureTask加入到mTasks 中
            tasks.add(new Runnable() {
                public void run() {
                    try {
                        // 最终会调用WorkerRunnable的call方法。
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (active == null) {
                // 当任务执行完或者当前没有活动的任务时都会执行scheduleNext方法
                scheduleNext();
            }
        }
        
    	// 从mTasks取出 FutureTask任务并交由 THREAD_POOL_EXECUTOR 处理
        protected synchronized void scheduleNext() {
            if ((active = tasks.poll()) != null) {
                executor.execute(active);
            }
        }
    }}
    

    前面我们提到call方法最终会调用postResult方法将结果投递出去,postResult 方法的代码如下所示:

    private Result postResult(Result result) {
      // 创建消息,并通过Handler将消息发送出去  
      @SuppressWarnings("unchecked")
      Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
              new AsyncTaskResult<Result>(this, result));
      message.sendToTarget();
      return result;
    }
    

    从这里可以看出,AsyncTask中消息的发送是使用Handler,getHandler方法如下所示:

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

    在getHandler方法中创建了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;
        }
      }
    }
    

    在接收到MESSAGE_POST_RESULT消息后会调用AsyncTask的finish方法,代码如下所示:

    private void finish(Result result) {
      if (isCancelled()) {	// 如果AsyncTask任务被取消了,则执行onCancelled方法
        onCancelled(result);
      } else {	// 否则就调用onPostExecute方法
        onPostExecute(result);
      }
      mStatus = Status.FINISHED;
    }
    

    接着回头来看SerialExecutor,线程池 SerialExecutor主要用来处理排队,将任务串行处理。

    在SerialExecutor中调用 scheduleNext 方法时,将任务交给THREAD_POOL_EXECUTOR。它同样是一个线程池,用来处理任务,代码如下所示:

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

    THREAD_POOL_EXECUTOR 指的就是 threadPoolExecutor,其核心线程和线程池允许创建的最大线程数都是由CPU的核数来计算出来的。它采用的阻塞队列仍旧是LinkedBlockingQueue,容量为128。

    至此,AsyncTask原理分析完毕。

    Android 3.0及以上版本用 SerialExecutor作为默认的线程,它将任务串行地处理,保证一个时间段只有一个任务执行;而Android 3.0之前的版本是并行处理的。所以,在7.0之后不会出现任务超标而执行饱和策略的情况。

    如果想要在3.0以上版本使用并行的线程处理,可以使用如下代码:

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

    executeOnExecutor方法的源码如下:

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

    其中第一个参数不但可以传入基础的线程池外,还可以传入自定义的线程池,代码如下:

    Executor executor = new ThreadPoolExecutor(0, Integer.MAX_VALUE, 0L,
          TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());
    asyncTask.executeOnExecutor(executor, "");
    
  • 相关阅读:
    时钟同步显示屏(NTP时间显示屏)子母钟系统介绍
    NTP服务器(时钟服务器)为金融交通行业保驾护航
    Moco server Linux 端部署
    Linux 系统下搭建 Gitlab 服务器
    CentOS7 防火墙设置命令
    Allure 环境安装
    Pytest allure 生成测试报告
    Pytest allure 定制化测试报告
    Pytest allure excel 接口自动化框架
    科创人·36氪副总裁王坤:企服产品应重视使用者体验,36氪将推出中国版「魔力象限」
  • 原文地址:https://www.cnblogs.com/pengjingya/p/14948021.html
Copyright © 2020-2023  润新知