android基础-AsyncTask

AsyncTask分析

hi,大家好,我是爱吃香蕉的猴子,今天梳理一下,首先感谢郭神曾经写的文章,我也是早年通过这个文章入门的AsyncTask, 郭神的文章, 我这次也是基于代码刨析,基于Q的代码;


AsyncTask的基础用法,这里我从SystemUI找一段

    public static AsyncTask<?, ?, ?> checkPattern(final LockPatternUtils utils,
            final List<LockPatternView.Cell> pattern,
            final int userId,
            final OnCheckCallback callback) {
            //todo
        AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
            private int mThrottleTimeout;
            private List<LockPatternView.Cell> patternCopy;

            @Override
            protected void onPreExecute() {
                // Make a copy of the pattern to prevent race conditions.
                // No need to clone the individual cells because they are immutable.
                patternCopy = new ArrayList(pattern);
            }

            @Override
            protected Boolean doInBackground(Void... args) {
                try {
                    return utils.checkPattern(patternCopy, userId, callback::onEarlyMatched);
                } catch (RequestThrottledException ex) {
                    mThrottleTimeout = ex.getTimeoutMs();
                    return false;
                }
            }

            @Override
            protected void onPostExecute(Boolean result) {
                callback.onChecked(result, mThrottleTimeout);
            }

            @Override
            protected void onCancelled() {
                callback.onCancelled();
            }
        };
        task.execute();
        return task;
    }

先看看AsyncTask中的三个泛型参数 和四个方法:

  • params: 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
  • progress: 后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位。
  • Result: 当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型。
  • onPreExecute: 这个方法会在后台任务开始执行之间调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。
  • doInBackground: 后台执行,切入子线程执行耗时操作。
  • onPostExecute:当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用。
  • onCancelled: Task的取消;

从execute方法入手,查看源码:

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

进入executeOnExecutor方法查看:

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {
        if (mStatus != Status.PENDING) {
            switch (mStatus) {
            ......
            }
        }

        mStatus = Status.RUNNING;
        onPreExecute();
        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    }

在这里我们找到了onPreExecute方法,但是没有doInbackground方法,现在从exec.execute(mFuture)来入手,先看exec, 通过传入的参数sDefaultExecutor,可以确定exec就是private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR; 所以执行的execute方法就是:

public static final Executor SERIAL_EXECUTOR = new 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);
            }
        }
    }

就是SerialExecutor类中的execute方法,这部分的代码后面再分析,现在找一下mFuture

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

mFuture在AsyncTask的构造器中初始化,传入了参数mWorker,现在查看一下mWorker:

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);
                Result result = null;
                try {
                    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                    //noinspection unchecked
                    result = doInBackground(mParams);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

在初始化mWorkder的代码中,有一个call方法,Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 设置为后台线程,执行了doInBackground后,将执行后的返回值,传递到postResult中,那postResult执行了什么? 猜测将结果传递到主线程。

    private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

   private Handler getHandler() {
        return mHandler;
    }

        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

如果你已经熟悉了异步消息处理机制,这里使用mHandler对象发出了一条消息,消息中携带了MESSAGE_POST_RESULT常量和一个表示任务执行结果的AsyncTaskResult对象。mHandler就是主线程handler.


提示: ArrayDeque是不支持多线程访问的;
Now 我们来讲一下刚才的SERIAL_EXECUTOR的execute方法,可以看到,SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,如果我们一次性启动了很多个任务,首先在第一次运行execute()方法的时候,会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的尾部,然后判断mActive对象是不是等于null,第一次运行当然是等于null了,于是会调用scheduleNext()方法。在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如何又有新的任务被执行,同样还会调用offer()方法将传入的Runnable添加到队列的尾部,但是再去给mActive对象做非空检查的时候就会发现mActive对象已经不再是null了,于是就不会再调用scheduleNext()方法。
看一看offer()方法里传入的Runnable匿名类,这里使用了一个try finally代码块,并在finally中调用了scheduleNext()方法,保证无论发生什么情况,这个方法都会被调用。也就是说,每次当一个任务执行完毕后,下一个任务才会得到执行,SerialExecutor模仿的是单一线程池的效果,如果我们快速地启动了很多任务,同一时刻只会有一个线程正在执行,其余的均处于等待状态。
当然,也有解决的方案:

mTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
    static {
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                new SynchronousQueue<Runnable>(), sThreadFactory);
        threadPoolExecutor.setRejectedExecutionHandler(sRunOnSerialPolicy);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;
    }
    private static final RejectedExecutionHandler sRunOnSerialPolicy =
            new RejectedExecutionHandler() {
        public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
            android.util.Log.w(LOG_TAG, "Exceeded ThreadPoolExecutor pool size");
            // As a last ditch fallback, run it on an executor with an unbounded queue.
            // Create this executor lazily, hopefully almost never.
            synchronized (this) {
                if (sBackupExecutor == null) {
                    sBackupExecutorQueue = new LinkedBlockingQueue<Runnable>();
                    sBackupExecutor = new ThreadPoolExecutor(
                            BACKUP_POOL_SIZE, BACKUP_POOL_SIZE, KEEP_ALIVE_SECONDS,
                            TimeUnit.SECONDS, sBackupExecutorQueue, sThreadFactory);
                    sBackupExecutor.allowCoreThreadTimeOut(true);
                }
            }
            sBackupExecutor.execute(r);
        }
    };
private static final int BACKUP_POOL_SIZE = 5;

不过你可能会疑惑,使用executeOnExecutor方法为什么就可以并行,因为exec是ThreadPoolExecutor不再是SerialExecutor, 可以在JDK中查看一下execute方法

//ThreadPoolExecutor#execute
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
   int c = ctl.get();    //由它可以获取到当前有效的线程数和线程池的状态
/*1.获取当前正在运行线程数是否小于核心线程池,是则新创建一个线程执行任务,否则将任务放到任务队列中*/
    if (workerCountOf(c) < corePoolSize){
        if (addWorker(command, tre))     //在addWorker中创建工作线程执行任务
            return ;
        c = ctl.get();
    }
/*2.当前核心线程池中全部线程都在运行workerCountOf(c) >= corePoolSize,所以此时将线程放到任务队列中*/
    if (isRunning(c) && workQueue.offer(command))    {    //线程池是否处于运行状态,且是否任务插入任务队列成功
        int recheck = ctl.get();
     if (!isRunning(recheck) && remove(command))        //线程池是否处于运行状态,如果不是则使刚刚的任务出队
       reject(command);    //抛出RejectedExceptionException异常
     else if (workerCountOf(recheck) == 0)
       addWorker(null, false);
  }
/*3.插入队列不成功,且当前线程数数量小于最大线程池数量,此时则创建新线程执行任务,创建失败抛出异常*/
  else if (!addWorker(command, false)){
    reject(command);    //抛出RejectedExceptionException异常
  }
}
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory)

补充参数解释:

  1. corePoolSize:核心线程池的线程数量
  2. maximumPoolSize:最大的线程池线程数量
  3. keepAliveTime:线程活动保持时间,线程池的工作线程空闲后,保持存活的时间。
  4. unit:线程活动保持时间的单位。
  5. workQueue:指定任务队列所使用的阻塞队列
    6.ThreadFactory 线程池创建线程使用的工厂

经过上面的代码,我们先得出一个简单的结论:execute方法中的corePoolSize方法核心数是5,在执行中如果<5,则addWork, 这样实现了线程池的排队执行。 例如:线程池是5,则五个同时排队执行。


                                                    Code的搬运工V1.0
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值