对AsyncTask的深入了解

一、AsyncTask能够做什么?

官方介绍:AsyncTask enables proper and easy use of the UI thread. This class allows to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.(AsyncTask是android提供的一种异步消息处理的解决方案,能简化我们在子线程中更新UI控件,使用AsyncTask你将看不到任何关于操作线程的代码。)

二、AsyncTask中ThreadPoolExecutor的配置。

    //根据cpu的大小来配置核心的线程
    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());
        }

    };
    //任务执行前保存任务的队列,这个队列仅保存由execute提交的Runnable任务
    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);

三、ThreadPoolExecutor中的线程是如何调度的?

3.1 线程调度流程

当有新的任务到达时,如果当前线程池中的线程数量小于CORE_POOL_SIZE,线程池仍会去为当前任务创建一个线程直到线程池中的线程总数等于CORE_POOL_SIZE,这时这几个线程变为常驻线程;当线程池中的线程数量大于CORE_POOL_SIZE,并且sPoolWorkQueue 没有满时,那么这个任务就会被添加到sPoolWorkQueue 按照FIFO原则去排队,当有核心线程空闲出来了,会检查sPoolWorkQueue 是否有排队的,然后取出任务去默默地执行(这种情况下,如果线程池中有线程的空闲时间大于KEEP_ALIVE ,就会将其移除线程池,这样可以动态的调整线程池中的线程数量);如果当前线程池中的线程数量大于CORE_POOL_SIZE并且sPoolWorkQueue 满员了,但是线程总数小于MAXIMUM_POOL_SIZE,那么会给这个任务再创建一个线程去处理它;如果当前线程数量大于CORE_POOL_SIZE和MAXIMUM_POOL_SIZE ,并且sPoolWorkQueue 也满了,那么会通过线程池中的RejectedExecutionHandler使用一定的策略来做拒绝处理。

3.2 RejectedExecutionHandler的4种拒绝策略

①hreadPoolExecutor.AbortPolicy:
 当线程池中的数量等于最大线程数时抛出java.util.concurrent.RejectedExecutionException异常,涉及到该异常的任务也不会被执行。
②ThreadPoolExecutor.CallerRunsPolicy():
 当线程池中的数量等于最大线程数时,重试添加当前的任务;它会自动重复调用execute()方法。
③ThreadPoolExecutor.DiscardOldestPolicy():
 当线程池中的数量等于最大线程数时,抛弃线程池中工作队列头部的任务(即等待时间最久Oldest的任务),并执行新传入的任务。
④ThreadPoolExecutor.DiscardPolicy():
当线程池中的数量等于最大线程数时,丢弃不能执行的新加任务。

四、AsyncTask串并行问题

4.1 AsyncTask默认执行方式的历史变更

在Android1.5时,默认是串行的。
Android 1.6 -> Android 2.3.2, 默认是并行。
Android  3.0之后, 默认是串行。

4.2 为什么AsyncTask最终变成串行?

由于一个进程内的所有AsyncTask都公用一个线程池,如果同时有几个AsyncTask并发执行任务,并且这几个AsyncTask在各自的doInBackground中同时访问相同的资源,但是开发者并没有去做同步访问处理,那么会造成线程不安全,所以API的设计者为了避免开发者在使用AsyncTask时发生这个问题,才把默认的执行方式改成串行。当然,如果开发者确实需要并发执行并做了同步处理,那么可以使用executeOnExecutor(Executor exec, Params… params)函数(参数设定为AsyncTask.THREAD_POOL_EXECUTOR)来并发执行任务。

五、AsyncTask基本用法

5.1参数设置

通过继承AsyncTask设置子类的三个参数

public abstract class AsyncTask<Params, Progress, Result>
  • Params: 异步任务处理的参数。
  • Progress: 异步任务执行过程中返回给UI线程的进度,通过publishProgress()方法发送出去。
  • Result:异步任务执行完返回的结果。

5.2 几个重要的实现方法

(1)doInBackground
子类必须实现的抽象方法,在这个方法里可以执行以下耗时的操作,因为这里是在子线程下进行的。

(2)onPreExecute
执行后台耗时操作前会被调用。

(3)onProgressUpdate
通过这个方法我们可以拿到任务的进度值,当然,这需要在doInBackground中执行publishProgress()才行。

(4)onPostExecute
这个方法会在doInBackground走完之后会调用,这里我们可以拿到任务执行完成后的返回值。

5.3 取消任务

通过AsyncTask.cancel,调用FutureTask中的cancel方法取消任务。

public final boolean cancel(boolean mayInterruptIfRunning) {
        //设置当前任务状态为取消,AsyncTask.isCancelled()方法取的值就是此时设置的值。
        mCancelled.set(true);
        return mFuture.cancel(mayInterruptIfRunning);
    }


public boolean cancel(boolean mayInterruptIfRunning) {
        //检测当前状态是否是NEW,如果不是,说明任务已经完成或取消或中断,所以直接返回。
        if (!(state == NEW &&
              U.compareAndSwapInt(this, STATE, NEW,
                  mayInterruptIfRunning ? INTERRUPTING : CANCELLED)))
            return false;
        try {    // in case call to interrupt throws exception
            //如果mayInterruptIfRunning为true的时候,线程就会调用interrupt()方法,抛出异常。
            if (mayInterruptIfRunning) {
                try {
                    Thread t = runner;
                    if (t != null)
                    //调用interrupt方法,状态设置为INTERRUPTING,然后试着中断线程,完成后设置状态为INTERRUPTED
                        t.interrupt();
                } finally { // final state
                //通知等待线程的结果(因为FutureTask.get()法获得计算结果的唯一方法,如果计算没有完成,此方法会堵塞直到计算完成)
                    U.putOrderedInt(this, STATE, INTERRUPTED);
                }
            }
        } finally {
            finishCompletion();
        }
        return true;
    }
  • 线程处于休眠状态时,如果mayInterruptIfRunning = true, 那么正在执行的线程会被中断,抛出异常,但是执行的任务会一直执行完成,然后调用onCancelled()。如果是false,那么正在执行的线程不会被中断,任务执行完成后调用onCancelled()。
  • 线程不处于休眠时,那么mayInterruptIfRunning的值无论是什么,正在执行的线程都会完成任务,然后调用onCancelled()。在doInBackGround中通过isCancelled()方法判断当前任务是否被取消。

5.4 示例

private class DownLoadAsynTask extends AsyncTask<Void, Integer, String>{


        @Override
        protected String doInBackground(Void... params) {
            return null;
        }

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

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


        @Override
        protected void onPostExecute(String s) {
            super.onPostExecute(s);
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
        }
    }
        //串行
        new DownLoadAsynTask().execute();
        //并行
        new DownLoadAsynTask().executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);

六、AsyncTask内部是如何运作的?

6.1 从AsyncTask的构造函数入手
    /**
     * 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
                //通过doInBackground获取结果
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                //将结果发送出去
                return postResult(result);
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                try {
                    //get()表示获取mWorker的call的返回值
                    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);
                }
            }
        };
    }

通过注释我们也可以知道,实例化AsyncTask对象必须在主线程中。AsyncTask构造函数内部由WorkerRunnable和FutureTask组成。

  • WorkerRunnable: 是一个实现了Callable的抽象类,扩展了Callable多了一个Params参数.
private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }
 * The {@code Callable} interface is similar to {@link
 * java.lang.Runnable}, in that both are designed for classes whose
 * instances are potentially executed by another thread.  A
 * {@code Runnable}, however, does not return a result and cannot
 * throw a checked exception.

Callable和Runnable很相似,因为两者都是为类的实例设计的,可能被另一个线程去执行。但是Runnable没有返回值,不能捕获异常。

  • FutureTask
public class FutureTask<V> implements RunnableFuture<V>

public FutureTask(Callable<V> callable) {
        if (callable == null)
            throw new NullPointerException();
        this.callable = callable;
        this.state = NEW;       // ensure visibility of callable
    }

它实现了Runnable接口作为runnable被线程执行,并且将Callable作为构造函数的参数传入。这样组合的好处是,假设有一个很耗时的返回值需要计算,并且这个返回值不是立刻需要的话,就可以使用这个组合,用另一个线程去计算返回值,而当前线程在使用这个返回值之前可以做其它的操作,等到需要这个返回值时,再通过Future得到。FutureTask的run方法要开始回调WorkerRunable的call方法了,call里面调用doInBackground(mParams),终于回到我们后台任务了,调用我们AsyncTask子类的doInBackground(),由此可以看出doInBackground()是在子线程中执行的。

public void run() {
        if (state != NEW ||
            !U.compareAndSwapObject(this, RUNNER, null, Thread.currentThread()))
            return;
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                    result = null;
                    ran = false;
                    setException(ex);
                }
                if (ran)
                    set(result);
            }
        } finally {
            // runner must be non-null until state is settled to
            // prevent concurrent calls to run()
            runner = null;
            // state must be re-read after nulling runner to prevent
            // leaked interrupts
            int s = state;
            if (s >= INTERRUPTING)
                handlePossibleCancellationInterrupt(s);
        }
    }
6.2 AsyncTask.execute()
    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

execute其实最后都是调用的executeOnExecutor方法,executeOnExecutor中两个参数,一个是SerialExecutor对象,实现了串行线程队列;另一个是传递的参数交由doInBackground去处理。

@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默认是SerialExecutor。如果想并发执行,可以通过executeOnExecutor将exec设为THREAD_POOL_EXECUTOR即可。上面代码中涉及到AsyncTask的三种状态:

  • Status.PENDING: 待执行状态。当AsyncTask被创建时,就进入了PENDING状态。
  • Status.RUNNING:运行状态。当调用executeOnExecutor,就进入了RUNNING状态。
  • Status.FINISHED: 结束状态。当AsyncTask完成(用户cancel()或任务执行完毕)时,就进入了FINISHED状态。

下面我们针对exec.execute(mFuture)来说说exec = SerialExecutor和exec = THREAD_POOL_EXECUTOR两种情况的execute内部是如何运行的。

  • SERIAL_EXECUTOR.execute(mFuture)
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);
            }
        }
    }

假设FutureTask插入进了两个以上的任务队列到mTasks中,第一次过来mActive==null,通过mTasks.poll()取出一个任务丢给线程池运行,线程池执行r.run,其实就是执行FutureTask的run方法,因为传递进来的r参数就是mFuture。等到上一个线程执完r.run()完之后,这里是通过一个try-finally代码块,并在finally中调用了scheduleNext()方法,保证无论发生什么情况,scheduleNext()都会取出下一个任务执行。接着因为mActive不为空了,不会再执行scheduleNext(),由于存在一个循环队列,每个 Runnable 被执行的时候,都进入去队列,然后在执行完后出队,才会进入下一个 Runnable 的执行流程。由此可知道这是一个串行的执行过程,同一时刻只会有一个线程正在执行,其余的均处于等待状态。

  • THREAD_POOL_EXECUTOR.execute(mFuture)
    整个过程就是上面3.1提到的。
public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        /*
         * Proceed in 3 steps:
         *
         * 1. If fewer than corePoolSize threads are running, try to
         * start a new thread with the given command as its first
         * task.  The call to addWorker atomically checks runState and
         * workerCount, and so prevents false alarms that would add
         * threads when it shouldn't, by returning false.
         *
         * 2. If a task can be successfully queued, then we still need
         * to double-check whether we should have added a thread
         * (because existing ones died since last checking) or that
         * the pool shut down since entry into this method. So we
         * recheck state and if necessary roll back the enqueuing if
         * stopped, or start a new thread if there are none.
         *
         * 3. If we cannot queue task, then we try to add a new
         * thread.  If it fails, we know we are shut down or saturated
         * and so reject the task.
         */
        int c = ctl.get();
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }
6.3 AsyncTask.postResult
private Result postResult(Result result) {
        @SuppressWarnings("unchecked")
        Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
                new AsyncTaskResult<Result>(this, result));
        message.sendToTarget();
        return result;
    }

通过getHandler得到一个handler,然后获得一个消息将结果封装到Message中,然后将消息发送到主线程中。

private static Handler getHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                //初始化一个InternalHandler,用与将结果发送给主线程
                sHandler = new InternalHandler();
            }
            return sHandler;
        }
    }

private static class InternalHandler extends Handler {
        public InternalHandler() {
            //获取主线程的Looper对象
            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;
            }
        }
    }

在handleMessage中根据消息类型去做相应的处理。

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

如果任务已经取消了,调用onCancelled方法,如果没被取消,则调用onPostExecute()方法。

@WorkerThread
    protected final void publishProgress(Progress... values) {
        if (!isCancelled()) {
            getHandler().obtainMessage(MESSAGE_POST_PROGRESS,
                    new AsyncTaskResult<Progress>(this, values)).sendToTarget();
        }
    }

如果doInBackground(Void… params)调用publishProgress()方法,实际就是发送一条MESSAGE_POST_PROGRESS消息,就会去执行onProgressUpdate()方法。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值