AsyncTask的分析,及使用

前些天面试被问到一些关于AsyncTask的问题,下面是我整理的一些关于这个的知识。

要了解一个类的使用首先可以通过类前面的介绍来有个大概的了解,然后再有针对的去看自己感兴趣的地方。那么下面进入正题^^

AsyncTask enables proper and easy use of the UI thread. This class allows you
* to perform background operations and publish results on the UI thread without
* having to manipulate threads and/or handlers

这句话相信接触过这个类的同学一看就都明白了,这里我请允许我再重复的描述下: 在ui线程中可以合适和简单的使用AsyncTask。AsyncTask允许我们可以在后台做一些操作(这里指比较耗时的操作)并且将结果发布到UI线程,而不需要人为的操作线程和使用handler.

AsyncTask 是定位是一个助手,非常适合处理一些几秒之内可以完成的短的操作,下面是原文AsyncTasks should ideally be used for short operations (a few seconds at the most.如果你的操作耗时比较长,建议使用Java提供的另外的并发工具类,如:Executor,ThreadPoolExecutor,FutureTask等。

AsyncTask是一个抽象类,所以必须以子类的形式使用。

下面粘贴一个简单的使用

private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
   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));
           // Escape early if cancel() is called
           if (isCancelled()) break;
       }
       return totalSize;
   }
   protected void onProgressUpdate(Integer... progress) {
       setProgressPercent(progress[0]);
   }
   protected void onPostExecute(Long result) {
       showDialog("Downloaded " + result + " bytes");
   }
}

从上面我们可以看到子类重写了onPreExecute,doInBackground,onProgressUpdate,onPostExecute等方法。下面简单的记录下这几个方法的功能以及所在线程,就进入分析阶段了。

  1. onPreExecute()是第一执行的方法,主要可以做一些初始化的操作,所在线程 Main
  2. doInBackground(),是主要的方法,每个子类都要实现,可以用来做些比较耗时的操作,所在线程 Work 线程
  3. onProgressUpdate() 主要做一些更新进度的操作,可以重写也可以不重写,看需求,所在线程 Main
  4. onPostExecute(),一般都会重写,主要是根据后台操作获得数据更新UI,所在线程 Main
执行 new DownloadFilesTask().execute(url1, url2, url3);
取消 cancel(boolean)

操作需要借助isCancelled()方法,一般是在doInBackground方法中添加判断,用法见上面的例子。其中源码中是这样描述的:

To ensure that a task is cancelled as quickly as possible, you should always
 * check the return value of {@link #isCancelled()} periodically from
 * {@link #doInBackground(Object[])}, if possible (inside a loop for instance.)

讲了一大堆没有的东西,下面进入分析阶段。

首先我们从使用的入口方法,着手。介绍中有说,AsyncTask默认后台其实顺序执行的,但是也支持并行执行。下面我们一个一个分析。

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

相信这个方法大家都很熟悉,有没有发现这个方法中调用的另一个方法中有一个sDefaultExecutor,让我们跟踪下代码看看它的实现细节

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;


/**
     * 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();
    
    
    
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,下面我们接着看下它的实现细节,

可以看到 内部创建了一个ArrayDeque mTasks ,不熟悉这个类,没关系,我们简单看下这个类的描述。

Resizable-array implementation of the {@link Deque} interface. 

可以看到它实现了deque接口,接着看deque描述

A linear collection that supports element insertion and removal at
both ends.

意思是是一种支持首尾操作的集合,其实它是对Queue的一个包装。这里用到的隐含的装饰者模式。

那么回到之前的地方,我们接着看execute方法,可以看到该方法加了同步锁,这是由于刚刚介绍的ArrayDeque不是一个线程安全的集合。

mTasks.offer,我们点进去看下介绍,

/**
     * Inserts the specified element at the end of this deque.
     *
     * <p>This method is equivalent to {@link #offerLast}.
     *
     * @param e the element to add
     * @return {@code true} (as specified by {@link Queue#offer})
     * @throws NullPointerException if the specified element is null
     */
    public boolean offer(E e) {
        return offerLast(e);
    }

可以看到就是插入一个数据到队列的尾部。

这里我们接着回到入口方法处,然后接着跟踪方法的执行

/**
     * Executes the task with the specified parameters. The task returns
     * itself (this) so that the caller can keep a reference to it.
     * 
     * <p>This method is typically used with {@link #THREAD_POOL_EXECUTOR} to
     * allow multiple tasks to run in parallel on a pool of threads managed by
     * AsyncTask, however you can also use your own {@link Executor} for custom
     * behavior.
     * 
     * <p><em>Warning:</em> Allowing multiple tasks to run in parallel from
     * a thread pool is generally <em>not</em> what one wants, because the order
     * of their operation is not defined.  For example, if these tasks are used
     * to modify any state in common (such as writing a file due to a button click),
     * there are no guarantees on the order of the modifications.
     * Without careful work it is possible in rare cases for the newer version
     * of the data to be over-written by an older one, leading to obscure data
     * loss and stability issues.  Such changes are best
     * executed in serial; to guarantee such work is serialized regardless of
     * platform version you can use this function with {@link #SERIAL_EXECUTOR}.
     *
     * <p>This method must be invoked on the UI thread.
     *
     * @param exec The executor to use.  {@link #THREAD_POOL_EXECUTOR} is available as a
     *              convenient process-wide thread pool for tasks that are loosely coupled.
     * @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}.
     *
     * @see #execute(Object[])
     */
    @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;
    }

上面的几句先先不用管,只是一些对状态的检验和修改,

onPreExecute();这个方法不就是使用时可以实现的方法吗,这也说明这个方法是确实是第一个执行的方法,而且运行线程在方法调用处所在的线程中,由于AsyncTask明确规定调用必须在UI线程,所以这里其实是UI线程。

mWorker.mParams = params; 可以看到我们传入的参数赋值给了一个叫mWorker的mParams,这里可以简单的看下这个mWorker

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
        Params[] mParams;
    }

可以看到实现了Callable接口,这里我们需要了解下Callable的特点,那么我们接着跟进去

从类头描述可以知道这个是一个可以返回值的Task
A task that returns a result and may throw an exception.

继续回到刚刚的方法处,
exec.execute(mFuture);可以看到我们穿了一个mFuture对象进去,我们跟踪下这个对象,最后在构造方法中找到

/**
     * Creates a new asynchronous task. This constructor must be invoked on the UI thread.
     *
     * @hide
     */
    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);
                    Binder.flushPendingCommands();
                } catch (Throwable tr) {
                    mCancelled.set(true);
                    throw tr;
                } finally {
                    postResult(result);
                }
                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有个了解:

A cancellable asynchronous computation.  This class provides a base
implementation of {@link Future}, with methods to start and cancel
a computation, query to see if the computation is complete, and
retrieve the result of the computation.  The result can only be
retrieved when the computation has completed; the {@code get}
methods will block if the computation has not yet completed.  Once
the computation has completed, the computation cannot be restarted
or cancelled (unless the computation is invoked using
{@link #runAndReset}).

一个可以被取消的异步计算。提供了对Future的基本实现,可以开始,取消一个计算,并且可以查询计算是否完成,并进而获取到计算结果。

好,带着上面的认识,我们回到SerialExecutor类的实现处看,

public synchronized void execute(final Runnable r) {
           mTasks.offer(new Runnable() {
               public void run() {
                   try {
                       r.run();
                   } finally {
                       scheduleNext();
                   }
               }
           });
           if (mActive == null) {
               scheduleNext();
           }
       }

可以看到,这里的r就是我们穿进去的mFuture,由上面我们对FeatureTask的认识,我们知道在一个线程中任务的执行是从上到下的顺序执行的,那么,这里r.run(),行完,才会走finally中的操作,虽然一进来会走if (mActive == null) {
scheduleNext();
}但是,刚进来时mTaks是空的,再来看

protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }

可以看到,第一次进来mActive是获取null,因而这个保证了任务的顺序执行。

了解了顺序执行,那我们在来了解下并行的执行是怎么实现的,虽然Android中这种用法很少,但是万一呢,

其实并的原理很简单,只是换了一个Executor,我们看另一个入口方法,其实刚刚有粘贴代码,为了方便查看,这里请允许我再粘贴一次。

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,这里其实AsyncTask内部为我们提供了另外一个Executor,就是THREAD_POOL_EXECUTOR,我们可以直接将他传进去,就可以试下并行的处理效果。下面是它的实现。

/**
     * An {@link Executor} that can be used to execute tasks in parallel.
     */
    public static final Executor THREAD_POOL_EXECUTOR;

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

可以看到THREAD_POOL_EXECUTOR是个静态的常量,因此可以直接使用。

其内部线程间的切换其实使用的是handler ,仔细看我们之前看过的代码,不难发现,有个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);



private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }

 
 
    

这里相信读者应该可以轻松的自己追踪,就不再赘述了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值