Android 笔记之 AsyncTask

AsyncTask
        AsyncTask 是一个轻量级的异步任务类,它可以在线程池中执行后台任务,然后把执行的进入和最终结果传递给主线程并在主线程中更新UI。它封装了Thread 和Handler,但是AsyncTask 不适合执行特别耗时的后台任务,对于特别耗时的任务建议采取使用线程池。

AsyncTask是一个抽象类,所以如果我们想使用它,就必须要创建一个子类去继承它。在继承时我们可以为AsyncTask类指定三个泛型参数,这三个参数的用途如下,如果不需要某个参数,可以设置为 Void

1. Params(参数类型)

在执行AsyncTask时需要传入的参数,可用于在后台任务中使用,是excute()任务执行方法和 doInBackground(Params...)的输入参数,通常为String

2. Progress(后台任务执行的进度类型)

后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位,是 onProgressUpdate(Progress...)和 publishProgress(Progress...)方法的参数,是一个数组类型的参数,一般为Integer

3. Result(返回的结果类型)

当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型,是onPostExecute(Result)方法的输入参数

如果AsyncTask 确实不需要传递具体的参数,这三个泛型参数可以使用Void 替代。

AsyncTask 的声明如下所示:

public abstract class AsyncTask<Params, Progress, Result> {
因此,一个最简单的自定义AsyncTask就可以写成如下方式:

class DownloadTask extends AsyncTask<Void, Integer, Boolean> {
……
}

这里我们把AsyncTask的第一个泛型参数指定为Void,表示在执行AsyncTask的时候不需要传入参数给后台任务。第二个泛型参数指定为Integer,表示使用整型数据来作为进度显示单位。第三个泛型参数指定为Boolean,则表示使用布尔型数据来反馈执行结果。

一个异步任务的执行一般包括以下几个步骤。

1.execute(Parmas...parmas)

    执行一个异步任务,需要我们在代码中调用此方法,触发异步任务的执行

2. onPreExecute()

执行在 UI 线程,在execute(Parmas...parmas)被调用后立即执行,这个方法会在后台任务开始执行之前调用,调用后立即执行,用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

3. doInBackground(Params...)

在线程池中执行,onPreExecute() 完成后立即执行,这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务。此方法将接收输入参数和返回计算结果,并将计算结果给onPostExecute 方法。在执行过程中可以调用 publishProgress(Progress...) 方法来更新任务进度信息。publishProgress 会调用 onProgressUpdate 方法。

4. onProgressUpdate(Progress...)

执行在 UI 线程,当在后台任务中调用了 publishProgress(Progress...) 方法后,这个方法就会很快被调用,直接将进度信息更新到 UI 组件上

5. onPostExecute(Result)

执行在 UI 线程,当后台操作结束时,该方法被调用,doInBackground(Params...) 函数返回的计算结果将作为参数传递到此方法中,直接将结果显示到 UI 组件上比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

上面这几个方法,onPreExecute 先执行,接着是doInBackground,最后才是onPostExecute。除了上述四个方法以外,AsyncTask 还提供了 onCancelled 方法,它同样执行在UI 线程,当异步任务被取消时,onCancelled 方法会被调用,此时onPostExecute 方法则不会被调用。如下所示:
public class DownloadFilesTask
        extends AsyncTask<URL, Integer, Long>
{
    //执行在线程池中
    @Override
    protected Long doInBackground(URL... urls) {
        int  count     = urls.length;
        long totalSize = 0;
        for (int i = 0; i < count; i++) {
            totalSize += Downloader.downloadFile(urls[i]);
            publishProgress((int) (i / (float) count) * 100);//更新下载任务进度
            if (isCancelled()) {//判断下载任务是否取消
                break;
            }
        }
        return totalSize;
    }

    @Override
    protected void onProgressUpdate(Integer... progress) {
        setProgressPercent(progress[0]);
    }

    @Override
    protected void onPostExecute(Long result) {
        showDialog("Downloaded" + result + "bttes");
    }
}
        注意doInBackground 和 onProgressUpdate 方法他们的参数中均包含 ... 的字样,在Java 中 ... 表示参数的数量不定,它是一种数组类型的参数。当要执行上述下载任务时,可以通过如下方式来完成:
   new DownloadFilesTask().execute(url1,url2.url3);

在使用的时候,有以下几点需要格外注意:
1、AsyncTask 对象必须在  UI  线程中创建
2、execute(Parmas...parmas)方法必须在 UI 线程中调用
3、不能在 doInBackground(Params...) 中更改 UI 组件信息
4、不要在程序中直接调用 onPreExecute()、onPostExecute()、doInBackground() 和 onProgressUpdate() 方法
5、一个任务实例只能执行一次,如果执行第二次将会抛出异常
6、在Android1.6 之前,AsyncTask 是串行执行任务的,在Android 1.6 AsyncTask 采用线程池里处理并行任务,但从Android 3.0 开始,为避免AsyncTask 所带来的并发错误,AsyncTask 又采用一个线程来串行执行任务。在Android 3.0 之后的版本中,我们仍然可以通过AsyncTask 的 executeOnExecutor 方法来并行的执行任务。


AsyncTask 的工作原理:

        分析AsyncTask 的工作原理,从它的excute 方法开始,如下所示:
  private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
      public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

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

        execute 方法会调用 executeOnExecutor 方法,源码如下所示:

    
    private final WorkerRunnable<Params, Result> mWorker;
    @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;//将参数赋值给mWorker
        exec.execute(mFuture);//mFuture 会交给 SerialExecutor 的 execute 方法处理

        return this;
    }
        在上述代码中,sDefaultExecutor 实际上是一个串行的线程池,一个进程中所有的AsyncTask 全部在这个串行的线程池中排队执行,在 executeOnExecutor 方法中,AsyncTask 的onPreExecute 方法最先执行,然后线程池开始执行,其执行过程如下所示:
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    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) {
            //将FutureTask 对象插入到任务队列 mTasks 中,
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            //如果当前没有正在活动的AsyncTask,则调用scheduleNext 
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {//执行下一个AsyncTask 任务
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
        从上面代码可知,首先系统会把AsyncTask 的Params 参数封装为 FutureTask 对象,FutureTask 是一个并发类,在这里充当了Runnable 的作用。接着这个 FutureTask 会交给 SerialExecutor 的 execute 方法处理,SerialExecutor 的 execute 方法首先会把FutureTask 对象插入到任务队列 mTasks 中,如果这个时候没有正在活动的AsyncTask,则调用scheduleNext方法来执行下一个AsyncTask 任务。 同时当一个AsyncTask 任务执行完后,AsyncTask 会继续执行其他任务知道所有的任务都被执行为止,从这点可以看出,默认情况下,AsyncTask 是串行执行的。

        AsyncTask 中有两个线程池(SerialExecutor 和 THREAD_POOL_EXECUTOR )和一个 Handler(InternalHandler),其中线程池SerialExecutor 用于任务的排队,线程池 THREAD_POOL_EXECUTOR  用于正真的执行任务,InternalHandler 用于将执行环境荣线程池切换到主线程。THREAD_POOL_EXECUTOR  的定义如下所示:
public static final Executor THREAD_POOL_EXECUTOR;
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();//CPU核心数
    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 BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);//任务队列容量

    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;
    }
        AsyncTask 对 THREAD_POOL_EXECUTOR 的配置规格如上所示。

      AsyncTask 的构造方法如下所示:
    public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper()
            ? getMainHandler()
            : new Handler(callbackLooper);

        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);//调用doInBackground 并获取返回值
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);//调用postResult 方法,并将返回值作为参数传递过去
                }
                return result;
            }
        };

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

        由于FutureTask 的run 方法会调用 mWorke 的call 方法,因此 mWorke 的call 方法最终会在线程池中执行。 FutureTask 的run 方法如下所示:

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

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

        在mWorke 的call 方法中,首先将  mTaskInvoked 设未 true,表明当前任务已经被调用过了,然后执行AsyncTask的 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;
    }
        postResult 方法会通过 mHandler 发送一个 MESSAGE_POST_RESULT的消息,mHandler 的定义如下所示:
    private static class InternalHandler extends Handler {
        public InternalHandler(Looper looper) {
            super(looper);
        }

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

        mHandler 是一个静态的Handler 对象,为了能够将执行环境切换到主线程,这就要求 mHandler 这个对象必须在主线程中创建。由于静态成员会在加载类的时候进行初始化,因此这就变相的要求 AsyncTask 的类必须在主线程中加载,否则同一个进程中的 AsyncTask 都将无法正常工作。
        mHandler 接收到 MESSAGE_POST_RESULT 消息后会调用 AsyncTask 的finish 方法,如下所示:
    private void finish(Result result) {
        if (isCancelled()) {
            onCancelled(result);
        } else {
            onPostExecute(result);
        }
        mStatus = Status.FINISHED;
    }
        从上述代码可知,如果AsyncTask 被取消了,就调用 onCancelled 方法,否则就会调用 onPostExecute 方法,而onPostExecute 方法的参数就是 doInBackground 方法的返回值,以上就是AsyncTask 的整个工作过程。

猜你喜欢

转载自blog.csdn.net/daxiong25/article/details/80538670