现在的位置: 首页 > 综合 > 正文

异步任务AsyncTask

2013年09月03日 ⁄ 综合 ⁄ 共 7412字 ⁄ 字号 评论关闭

AsyncTask是Android为我们提供的用途非常广的类。如果你需要另启线程来完成一个任务时,你可以考虑用AsyncTask。

当然,网上对AsyncTask类的用法讲解也非常多,本人也从网上学到了很多,所以这里就不赘述用法了,这里只想跟大家分享一下对AsyncTask内部实现的理解。

简单得说,AsyncTask内部实现主要依靠两样东西——线程池和Handler。由于本人学疏才浅,加之对于线程池的接触不多,有关线程池的知识请自行查阅相关书籍。文中如有理解不当请指正!谢谢~~~~

那么首先我们看看AsyncTask中定义了哪些字段?

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 = 10;

    //用于线程池的缓冲队列,长度为10
    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());
        }
    };

    //AsyncTask中的一个“主角”,管理线程的指挥官——线程池对象
    private static final ThreadPoolExecutor sExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE,
            MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sWorkQueue, sThreadFactory);

    //用于“后台”与Handler之间通信的标识,以便Handler做出正确的处理
    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;
    private static final int MESSAGE_POST_CANCEL = 0x3;

    //AsyncTask中的另一个“主角”,用于后台线程与UI线程进行交互的Handler对象
    private static final InternalHandler sHandler = new InternalHandler();

    //WorkerRunnable实现了Callable接口,后台任务就是在其中执行
    private final WorkerRunnable<Params, Result> mWorker;
    //FutureTask是一个异步的可取消的计算任务,可以将一个Callable或者Runnable对象封装在内部执行其中的线程任务。
    //可以随时查询计算的结果。
    private final FutureTask<Result> mFuture;

    //Status表示了当前任务的状态
    //PENDING:即将开始任务
    //RUNNING:任务正在执行
    //FINISHED:任务执行完毕
    private volatile Status mStatus = Status.PENDING;

    /**
     * Indicates the current status of the task. Each status will be set only once
     * during the lifetime of a task.
     */
    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,
    }

其中WorkRunnable是一个实现了Callable接口的类

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        //WorkerRunnable的主要任务就是对mParams进行运算处理
        Params[] mParams;
    }

上面的代码主要的工作就是为我们准备了一个我们将要使用的线程池。下面我们再来看看构造方法中又做了什么工作。

/**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     */
    public AsyncTask() {
        //创建一个WorkerRunnable对象,其有两个泛型参数,分别对应AsyncTask中泛型参数的输入参数和结果参数。
	//call()方法中执行doInBackground()方法,也就是说,在这里我们完成了后台任务的执行,并且按我们自己的需求返回一个Result类型的结果。
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                return doInBackground(mParams);
            }
        };

	//创建一个FutureTask对象,将mWorker传入作为参数,当其执行异步计算任务时,会调用mWorker中的call方法获得结果
      	mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                Message message;
                Result result = null;

                try {
		//此时线程会阻塞,知道计算任务完成。期间会调用mWoker的call方法并返回Result
                    result = 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) {
		//当抛出CancellationException时,说明此时异步计算任务被中断了,此时向Handler对象发送消息,通知任务中断
                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
                    message.sendToTarget();
                    return;
                } catch (Throwable t) {
                    throw new RuntimeException("An error occured while executing "
                            + "doInBackground()", t);
                }

		//正常执行结束时,向Handler对象发送消息,通知任务执行完毕
                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                        new AsyncTaskResult<Result>(AsyncTask.this, result));
                message.sendToTarget();
            }
        };
    }

这样,构造方法分析完了。准备工作也做好了,那么,接下来我们就可以开始使用了。用过AsyncTask的执行是从execute(Params...params)方法开始的,那么现在看看execute方法中做了什么工作。

/**
     * Executes the task with the specified parameters. The task returns
     * itself (this) so that the caller can keep a reference to it.
     *
     * This method must be invoked on the UI thread.
     *
     * @param params The parameters of the task.
     *
     * @return This instance of AsyncTask.
     *
     * @throws IllegalStateException If {@link #getStatus()} returns either
     *         {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}.
     */
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        //此方法需要传入一个Params的参数,也就是AsyncTask定义的时候的第一个泛型参数类型
        //进入方法后首先判断当前任务的状态
        //如果mStatus的值为RUNNING或者FINISHED,就抛出IllegalStateException
        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.PENDING,即符合执行的状态后,开始执行任务,mStatus的值也变为RUNNING
        mStatus = Status.RUNNING;

        //onPreExecute()中通常进行准备工作
        onPreExecute();

        //将params值赋值给mWorker中的mParams,也就是给mWorker准备好了“材料”
        mWorker.mParams = params;
        
        //剩下的事情就是把mFuture交给线程池来处理了,当然这时候“后台任务”就开始了
        sExecutor.execute(mFuture);

        return this;
    }

从上面我们可以看出,在execute()中,做的工作很简单,

1 . 执行准备工作——执行onPreExecute()方法,给mWorker传初值。其中onPreExecute()方法中的内容需要我们自己来实现。

2 . 将新线程放入线程池中。 

线程放入线程池之后,接下来的工作就是线程中执行的工作了,现在回过头看mFuture中的实现过程:

//创建一个FutureTask对象,将mWorker传入作为参数,当其执行异步计算任务时,会调用mWorker中的call方法获得结果
      	mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
                Message message;
                Result result = null;

                try {
		//此时线程会阻塞,知道计算任务完成。期间会调用mWoker的call方法并返回Result
                    result = 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) {
		//当抛出CancellationException时,说明此时异步计算任务被中断了,此时向Handler对象发送消息,通知任务中断
                    message = sHandler.obtainMessage(MESSAGE_POST_CANCEL,
                            new AsyncTaskResult<Result>(AsyncTask.this, (Result[]) null));
                    message.sendToTarget();
                    return;
                } catch (Throwable t) {
                    throw new RuntimeException("An error occured while executing "
                            + "doInBackground()", t);
                }

		//正常执行结束时,向Handler对象发送消息,通知任务执行完毕
                message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
                        new AsyncTaskResult<Result>(AsyncTask.this, result));
                message.sendToTarget();
            }
        };

无论是被中断了,或者是正常执行结束,最后都交由InternalHandler的对象sHandler进行处理,那么InternalHandler中又作了什么处理呢?在分析InternalHandler之前,我们还得看一个类AsyncTaskResult,它将所得的结果封装了起来,统一交给InternalHandler进行处理。

private static class AsyncTaskResult<Data> {
        final AsyncTask mTask;
        final Data[] mData;

        AsyncTaskResult(AsyncTask task, Data... data) {
            mTask = task;
            mData = data;
        }
    }

接下来就可以进入InternalHandler了。

private static class InternalHandler extends Handler {
        @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
        @Override
        public void handleMessage(Message msg) {
            //获得封装好的结果
            AsyncTaskResult result = (AsyncTaskResult) msg.obj;
            switch (msg.what) {
                case MESSAGE_POST_RESULT:
                    //任务完成,执行finish方法
                    result.mTask.finish(result.mData[0]);
                    break;
                case MESSAGE_POST_PROGRESS:
                    //更新进度,执行onProgressUpdate方法
                    result.mTask.onProgressUpdate(result.mData);
                    break;
                case MESSAGE_POST_CANCEL:
                    //任务取消,执行onCancelled方法
                    result.mTask.onCancelled();
                    break;
            }
        }
    }

那么,在InternalHandler中就有三种情况:

1,如果任务顺利完成了,那么最后收尾工作就交给finish方法来完成。

private void finish(Result result) {
        if (isCancelled()) result = null;
        
        //最后结果将传给onPostExecute
        onPostExecute(result);
        mStatus = Status.FINISHED;
    }

而onPostExecute也是通常需要我们自己实现的,里面自然是对结果进行处理了。

2,如果更新进度,则需要执行onProgressUpdate方法。

可是从上面的流程中我们并没有看到进度更新是在什么时候执行的,难道我们遗漏了???不是的,通常更新进度的都是更具具体需求来实现的,因此更不更新进度由我们说的算。如果我们需要更新进度,通常的做法是在doInBackgound方法中调用publishProgress方法。这个同样是在后台完成的。

protected final void publishProgress(Progress... values) {
        //向InternalHandler的对象发送消息,表示需要更新进度
        sHandler.obtainMessage(MESSAGE_POST_PROGRESS,
                new AsyncTaskResult<Progress>(this, values)).sendToTarget();
    }

InternalHandler的对象收到消息后,接着会onProgressUpdate方法,在其中进行具体的操作。而onProgressUpdate方法也是需要我们来进行实现的。

3,如果被中断,则执行onCancelled方法,在其中进行一些相关的处理。

这样AsyncTask主要的执行流程就分析完毕了,我们如果要使用AsyncTask的话,只需要重载onPreExecute,doInBackground,onProgressUpdate,onCancelled和onPostExecute方法就可以,非常的方便。

好了,以上内容如果有不正确的地方还望指正~~~~~~~

抱歉!评论已关闭.