Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android AsyncTask使用步骤与源码分析

$
0
0

AsyncTask的一个典型的应用场景是:后台下载文件,并实时跟新下载进度。它既能使得耗时的事情在后台线程中执行,又能和主线程通信,告诉主线程更新UI。同时,AsyncTask内部使用了线程池来执行后台任务,因此它能处理多任务请求。那么它的内部是怎么实现的呢?

使用步骤

在阅读源码之前,我们还是看一下AsyncTask的使用步骤:
这部分参考了AsyncTask的基本用法 这篇博客。

1.子类化AsyncTask

2.实现AsyncTask中定义的下面一个或几个方法

2.1onPreExecute(), 该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。
2.2doInBackground(Params…), 将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。
2.3onProgressUpdate(Progress…),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。
2.4onPostExecute(Result), 在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

3.提交任务请求

使用executeOnExecutor后者execute方法。
同时,我们还需要注意一下几点:
  A)Task的实例必须在UI thread中创建
  B) execute方法必须在UI thread中调用
  C) 不要手动的调用onPreExecute(), onPostExecute(Result),doInBackground(Params…), onProgressUpdate(Progress…)这几个方法
  D) 该task只能被执行一次,否则多次调用时将会出现异常

源码分析

AsyncTask的源码不多,因此分析比较容易。我之所以要分析这个类的源码是因为我遇到了这样的困惑:既然AsyncTask使用线程池来处理多任务请求,那么我提交多个任务是不是应该这样呢?

        //1 创建一个AsyncTask 的实例
        AsyncTask asyncTask = new XXAsyncTask()
        //2 提交多个任务
        asyncTask.execute(1);
        asyncTask.execute(2);
        asyncTask.execute(3);
        asyncTask.execute(4);
        asyncTask.execute(5);

很不幸的是,程序立刻就挂掉了,问什么呢?我想的,既然AsyncTask内部有个线程池,那么我不断给它提交任务不就可以了吗?而事实证明这样不行,那么正确的姿势是怎么样的呢?
比如要提交10个任务:

    for(int i=0;i<10;i++){
        //1 创建一个AsyncTask 的实例
        AsyncTask asyncTask = new XXAsyncTask()
        //2 提交多个任务
        asyncTask.execute(i);
    }

也就是说要不断的new AsyncTask 的实例,这是什么情况呢?难道不是每个AsyncTask都有一个线程池吗?还真不是的。
打开源码一看,立刻明白了,AsyncTask使用的是单例模式。

    /**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR
            = new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
                    TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);

    /**
     * An {@link Executor} that executes tasks one at a time in serial
     * order.  This serialization is global to a particular process.
     */
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();

AsyncTask构造了两个静态的线程池:THREAD_POOL_EXECUTOR和SERIAL_EXECUTOR 。因为它们是静态的,因此所有的AsyncTask对象都共享这两个线程池。也就是说,我已开始的理解就是错的,我以为一个AsyncTask内部有一个线程池,其实不然,而是所有的对象共享这两个线程池,其中SERIAL_EXECUTOR 是默认使用的线程池,当然我们可以选择使用THREAD_POOL_EXECUTOR作为线程池的,主要用到executeOnExecutor方法,比如:

asyncTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR,1);

理解了这点后,我们看看AsyncTask的具体内部实现。
当我们创建好AsyncTask实例后,采用默认的线程池的情况下,我们会执行excute方法,这个方法如下:

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

调用executeOnExecutor方法进一步处理,注意传给executeOnExecutor方法的参数sDefaultExecutor定义如下:

  private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

也就是说默认的线程池是SERIAL_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;
    }

该方法中出现的Status定义如下:

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

Status中只定义了三种状态,因此,asyncTask的状态如果不是Status.PENDING,就会抛出异常。这是为什么我们向下面这样使用程序会挂掉:

        //1 创建一个AsyncTask 的实例
        AsyncTask asyncTask = new XXAsyncTask()
        //2 提交多个任务
        asyncTask.execute(1);
        asyncTask.execute(2);
        asyncTask.execute(3);

第一次调用execute的时候,asyncTask的状态由Status.PENDING转为Status.RUNNING,第二次调用的是有,asyncTask的状态还是Status.RUNNING,因此程序就会挂掉了。

step 1

接下来会调用onPreExecute()方法,也就是说这个方法是在UI线程中调用的,我们完全可以在其中更新UI。做一个初始化工作,这个时候,我们提交的任务还没有执行。继续往下看。

step 2

接下来调用了:

exec.execute(mFuture);

exec就是excute方法中传入的sDefaultExecutor ,它默认初始化为SERIAL_EXECUTOR,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) {
            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);
            }
        }
    }

exec.excute方法也就是这里的execute方法了。这里构造了一个Runnable的实例并把它添加到mTasks 中,第一次执行到这里mActive 肯定为null,因此会调用scheduleNext方法,这个方法使用 mTasks.poll()去除之前添加的Runable,然后执行THREAD_POOL_EXECUTOR.execute(mActive),从而把Runable添加到线程池中。

step 3

Runnable中调用了传入的Runnable的run方法,这个Runable就是mFuture,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 是一个FutureTask的实例,并且这个实例接受一个mWork参数,这个mWork是一个和mFuture一样顶一个AsyncTask的构造方法中:

        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
                mTaskInvoked.set(true);

                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                Result result = doInBackground(mParams);
                Binder.flushPendingCommands();
                return postResult(result);
            }
        };

mWorker 其实是一个Callable的实例了,他有个call方法。而mFuture本质上是一个Runnable的实例,调用mFuture的run方法其实就是调用FutureTask中的run方法,FutureTask接受了mWorker 作为参数,并把它赋值给自己的callable 属性,代码如下:

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

step 4

所以我们看看FutureTask的run方法

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

这个方法做了如下事情:
1.调用callable的call方法,也就是mWork的call方法,mWork的call方法step 3中已经贴过,其中会调用doInBackground方法,也就是我们提交的需要在后台执行的代码了。因此,doInBackground是在mWork的call方法中被执行的,mWork的call方法又是通过THREAD_POOL_EXECUTOR.execute(mActive)被添加到线程池后被执行的。
2.调用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方法会发送一个消息,这个消息会使得InternalHandler的handleMessage方法被调用。这个时候,我们已经离开了后台线程,又回到UI线程了。因为InternalHandler的Looper是UI线程的Looper,其构造函数中有如下代码可以知晓:

        public InternalHandler() {
            super(Looper.getMainLooper());
        }

注意我们获取的消息为MESSAGE_POST_RESULT,因此根据handleMessage的定义:

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

会调用到AsyncTask的finish方法:

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

step 5

这个方法中,我们没有调用取消方法的话,就会调用onPostExecute方法了,这样,我们使用AsyncTask需要覆写的几个方法只有onProgressUpdate方法没有调用了。那么这个方法是怎么被调用的呢?我们说,我们要更新进度的话,需要使用publishProgress方法,我们看看这个方法:

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

这个方法就是发送一个消息,然后还是InternalHandler方法handleMessage方法被调用,注意这会消息是MESSAGE_POST_PROGRESS,因此根据源码,再贴一次handleMessage方法吧:

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

可见这次调用的是onProgressUpdate方法。至此,所有需要覆写的方法的调用过程我们都分析结束了。

作者:u011913612 发表于2016/11/15 19:20:22 原文链接
阅读:63 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>